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.
- data/README.md +2 -2
- data/lib/recurly.rb +2 -1
- data/lib/recurly/account.rb +2 -2
- data/lib/recurly/base.rb +30 -12
- data/lib/recurly/billing_info.rb +2 -2
- data/lib/recurly/exceptions.rb +13 -0
- data/lib/recurly/plan.rb +1 -1
- data/lib/recurly/subscription.rb +2 -2
- data/lib/recurly/transaction.rb +2 -2
- data/lib/recurly/transparent.rb +1 -1
- data/lib/recurly/verification.rb +77 -0
- data/lib/recurly/version.rb +1 -1
- data/spec/config/recurly.yml +4 -4
- data/spec/integration/account_spec.rb +12 -0
- data/spec/integration/billing_info_spec.rb +2 -2
- data/spec/integration/charge_spec.rb +0 -1
- data/spec/integration/plan_spec.rb +2 -2
- data/spec/support/vcr.rb +2 -2
- data/spec/unit/verification_spec.rb +76 -0
- data/spec/vcr/account/accept-language-account/1313708721.yml +39 -0
- data/spec/vcr/account/close/1313708721.yml +104 -0
- data/spec/vcr/account/create-blank/1313708721.yml +33 -0
- data/spec/vcr/account/create-duplicate/1313708721.yml +69 -0
- data/spec/vcr/account/create-min/1313708721.yml +38 -0
- data/spec/vcr/account/create/1313708721.yml +39 -0
- data/spec/vcr/account/find/1313708721.yml +107 -0
- data/spec/vcr/account/list/1313708721.yml +878 -0
- data/spec/vcr/account/update/1313708721.yml +158 -0
- data/spec/vcr/billing/create/1313708721.yml +118 -0
- data/spec/vcr/billing/destroy/1313708721.yml +177 -0
- data/spec/vcr/billing/errors/1313708721.yml +71 -0
- data/spec/vcr/billing/find/1313708721.yml +156 -0
- data/spec/vcr/billing/update/1313708721.yml +159 -0
- data/spec/vcr/charge/create/1313708721.yml +153 -0
- data/spec/vcr/charge/delete-uninvoiced/1313708721.yml +206 -0
- data/spec/vcr/charge/list-all/1313708721.yml +198 -0
- data/spec/vcr/charge/list-invoiced/1313708721.yml +294 -0
- data/spec/vcr/charge/list-pending/1313708721.yml +200 -0
- data/spec/vcr/charge/lookup/1313708721.yml +112 -0
- data/spec/vcr/coupon/create/1296774903.yml +96 -170
- data/spec/vcr/coupon/destroy/1296774903.yml +114 -190
- data/spec/vcr/credit/create/1313708090.yml +112 -0
- data/spec/vcr/credit/delete/1313708090.yml +171 -0
- data/spec/vcr/credit/list/1313708090.yml +198 -0
- data/spec/vcr/credit/lookup/1313708090.yml +112 -0
- data/spec/vcr/invoice/create-no-charges/1296674173.yml +100 -185
- data/spec/vcr/invoice/create/1296674173.yml +182 -328
- data/spec/vcr/invoice/list/1296674173.yml +255 -442
- data/spec/vcr/invoice/lookup/1296674173.yml +187 -331
- data/spec/vcr/plan/all.yml +210 -253
- data/spec/vcr/plan/find.yml +115 -187
- data/spec/vcr/plan/update.yml +105 -187
- data/spec/vcr/subscription/addons/add/1296674173.yml +269 -212
- data/spec/vcr/subscription/addons/create/1296674173.yml +172 -212
- data/spec/vcr/subscription/addons/remove/1296674173.yml +257 -212
- data/spec/vcr/subscription/cancel-with-code/1296674173.yml +271 -452
- data/spec/vcr/subscription/cancel/1296674173.yml +234 -394
- data/spec/vcr/subscription/change1/1296674173.yml +237 -402
- data/spec/vcr/subscription/change2/1296674173.yml +238 -402
- data/spec/vcr/subscription/create/1296674173.yml +123 -221
- data/spec/vcr/subscription/find/1296674173.yml +161 -279
- data/spec/vcr/subscription/reactivate/1296674173.yml +261 -431
- data/spec/vcr/subscription/refund-full/1296674173.yml +223 -373
- data/spec/vcr/subscription/refund-none/1296674173.yml +223 -373
- data/spec/vcr/subscription/refund-partial/1296674173.yml +223 -373
- data/spec/vcr/transaction/all/1311126815.yml +215 -0
- data/spec/vcr/transaction/create-no-account/1311126815.yml +51 -0
- data/spec/vcr/transaction/create-with-account/1311126815.yml +125 -0
- data/spec/vcr/transaction/list-filled/1311126815.yml +349 -0
- data/spec/vcr/transaction/list-initial/1311126815.yml +123 -0
- data/spec/vcr/transaction/lookup/1311126815.yml +335 -0
- data/spec/vcr/transaction/refund/1311126815.yml +163 -0
- data/spec/vcr/transaction/void/1311126815.yml +215 -0
- data/spec/vcr/transparent/post-url/1311239470.yml +76 -0
- metadata +207 -154
- data/spec/vcr/account/accept-language-account/1296674173.yml +0 -70
- data/spec/vcr/account/close/1296674173.yml +0 -164
- data/spec/vcr/account/create-blank/1296674173.yml +0 -52
- data/spec/vcr/account/create-duplicate/1296674173.yml +0 -116
- data/spec/vcr/account/create-min/1296674173.yml +0 -66
- data/spec/vcr/account/create/1296674173.yml +0 -70
- data/spec/vcr/account/find/1296674173.yml +0 -169
- data/spec/vcr/account/list/1296674173.yml +0 -55
- data/spec/vcr/account/update/1296674173.yml +0 -258
- data/spec/vcr/billing/create/1296690812.yml +0 -215
- data/spec/vcr/billing/destroy/1296690812.yml +0 -294
- data/spec/vcr/billing/find/1296690812.yml +0 -280
- data/spec/vcr/billing/update/1296690812.yml +0 -297
- data/spec/vcr/charge/create/1296674173.yml +0 -262
- data/spec/vcr/charge/delete-uninvoiced/1296674173.yml +0 -307
- data/spec/vcr/charge/list-all/1296674173.yml +0 -316
- data/spec/vcr/charge/list-invoiced/1296674173.yml +0 -468
- data/spec/vcr/charge/list-pending/1296674173.yml +0 -316
- data/spec/vcr/charge/lookup/1296674173.yml +0 -180
- data/spec/vcr/coupon/create/1296688818.yml +0 -183
- data/spec/vcr/coupon/destroy/1296688818.yml +0 -207
- data/spec/vcr/credit/create/1296674173.yml +0 -121
- data/spec/vcr/credit/delete/1296674173.yml +0 -121
- data/spec/vcr/credit/list/1296674173.yml +0 -121
- data/spec/vcr/credit/lookup/1296674173.yml +0 -121
- data/spec/vcr/plan/delete/1296674173.yml +0 -225
- data/spec/vcr/transaction/all/1296674173.yml +0 -389
- data/spec/vcr/transaction/create-no-account/1296674173.yml +0 -93
- data/spec/vcr/transaction/create-with-account/1296674173.yml +0 -225
- data/spec/vcr/transaction/list-filled/1296674173.yml +0 -487
- data/spec/vcr/transaction/list-initial/1296674173.yml +0 -217
- data/spec/vcr/transaction/lookup/1296674173.yml +0 -563
- data/spec/vcr/transaction/refund/1296674173.yml +0 -287
- data/spec/vcr/transaction/void/1296674173.yml +0 -352
- data/spec/vcr/transparent/post-url/1311127536.yml +0 -0
- data/spec/vcr/transparent/post-url/1311127583.yml +0 -0
- data/spec/vcr/transparent/post-url/1311128042.yml +0 -0
- data/spec/vcr/transparent/post-url/1311128073.yml +0 -0
- 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://
|
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.
|
83
|
+
"http://app.lvh.me:3000"
|
83
84
|
when :production
|
84
85
|
"https://api-production.recurly.com"
|
85
86
|
when :sandbox
|
data/lib/recurly/account.rb
CHANGED
@@ -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
|
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 =
|
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
|
-
|
82
|
-
|
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(
|
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
|
-
|
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
|
-
#
|
133
|
-
def self.instantiate_record(record, prefix_options)
|
134
|
-
|
135
|
-
|
136
|
-
|
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)
|
data/lib/recurly/billing_info.rb
CHANGED
@@ -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
|
42
|
+
super
|
43
43
|
end
|
44
44
|
|
45
45
|
def update_only
|
data/lib/recurly/exceptions.rb
CHANGED
@@ -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
data/lib/recurly/subscription.rb
CHANGED
@@ -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
|
20
|
+
super
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.refund(account_code, refund_type = :partial)
|
data/lib/recurly/transaction.rb
CHANGED
@@ -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
|
31
|
+
super
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.list(status = :all)
|
data/lib/recurly/transparent.rb
CHANGED
@@ -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
|
data/lib/recurly/version.rb
CHANGED
data/spec/config/recurly.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
---
|
2
|
-
username: api-test@
|
3
|
-
password:
|
4
|
-
private_key:
|
5
|
-
subdomain:
|
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[:
|
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
|
@@ -64,7 +64,7 @@ module Recurly
|
|
64
64
|
Plan.new({
|
65
65
|
:plan_code => "test",
|
66
66
|
:name => "Test Plan",
|
67
|
-
:
|
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.
|
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
@@ -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
|