spreedly 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/HISTORY.md +5 -0
- data/README.md +362 -29
- data/Rakefile +32 -0
- data/lib/certs/cacert.pem +7815 -0
- data/lib/spreedly.rb +24 -282
- data/lib/spreedly/common/errors_parser.rb +15 -0
- data/lib/spreedly/common/fields.rb +90 -0
- data/lib/spreedly/connection.rb +40 -0
- data/lib/spreedly/environment.rb +176 -0
- data/lib/spreedly/error.rb +50 -0
- data/lib/spreedly/gateway.rb +10 -0
- data/lib/spreedly/model.rb +17 -0
- data/lib/spreedly/payment_methods/credit_card.rb +9 -0
- data/lib/spreedly/payment_methods/payment_method.rb +34 -0
- data/lib/spreedly/payment_methods/paypal.rb +7 -0
- data/lib/spreedly/payment_methods/sprel.rb +7 -0
- data/lib/spreedly/ssl_requester.rb +65 -0
- data/lib/spreedly/transactions/add_payment_method.rb +16 -0
- data/lib/spreedly/transactions/auth_purchase.rb +17 -0
- data/lib/spreedly/transactions/authorization.rb +7 -0
- data/lib/spreedly/transactions/capture.rb +14 -0
- data/lib/spreedly/transactions/gateway_transaction.rb +31 -0
- data/lib/spreedly/transactions/purchase.rb +7 -0
- data/lib/spreedly/transactions/redact_payment_method.rb +14 -0
- data/lib/spreedly/transactions/refund.rb +14 -0
- data/lib/spreedly/transactions/retain_payment_method.rb +14 -0
- data/lib/spreedly/transactions/transaction.rb +41 -0
- data/lib/spreedly/transactions/void.rb +9 -0
- data/lib/spreedly/urls.rb +64 -0
- data/lib/spreedly/version.rb +1 -1
- data/spreedly.gemspec +29 -0
- data/test/credentials/credentials.yml +9 -0
- data/test/credentials/test_credentials.rb +43 -0
- data/test/helpers/assertions.rb +29 -0
- data/test/helpers/communication_helper.rb +31 -0
- data/test/helpers/creation_helper.rb +26 -0
- data/test/helpers/stub_response.rb +18 -0
- data/test/remote/remote_add_credit_card_test.rb +62 -0
- data/test/remote/remote_add_gateway_test.rb +30 -0
- data/test/remote/remote_authorize_test.rb +48 -0
- data/test/remote/remote_capture_test.rb +71 -0
- data/test/remote/remote_find_gateway_test.rb +31 -0
- data/test/remote/remote_find_payment_method_test.rb +29 -0
- data/test/remote/remote_find_transaction_test.rb +33 -0
- data/test/remote/remote_list_transactions_test.rb +36 -0
- data/test/remote/remote_purchase_test.rb +69 -0
- data/test/remote/remote_redact_test.rb +38 -0
- data/test/remote/remote_refund_test.rb +65 -0
- data/test/remote/remote_retain_test.rb +39 -0
- data/test/remote/remote_void_test.rb +64 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/add_credit_card_test.rb +74 -0
- data/test/unit/add_gateway_test.rb +26 -0
- data/test/unit/authorize_test.rb +87 -0
- data/test/unit/capture_test.rb +91 -0
- data/test/unit/environment_test.rb +18 -0
- data/test/unit/fields_test.rb +75 -0
- data/test/unit/find_gateway_test.rb +28 -0
- data/test/unit/find_payment_method_test.rb +90 -0
- data/test/unit/find_transaction_test.rb +31 -0
- data/test/unit/list_transactions_test.rb +46 -0
- data/test/unit/purchase_test.rb +95 -0
- data/test/unit/redact_payment_method_test.rb +51 -0
- data/test/unit/refund_test.rb +91 -0
- data/test/unit/response_stubs/add_credit_card_stubs.rb +43 -0
- data/test/unit/response_stubs/add_gateway_stubs.rb +39 -0
- data/test/unit/response_stubs/authorization_stubs.rb +139 -0
- data/test/unit/response_stubs/capture_stubs.rb +87 -0
- data/test/unit/response_stubs/find_gateway_stubs.rb +38 -0
- data/test/unit/response_stubs/find_payment_method_stubs.rb +108 -0
- data/test/unit/response_stubs/find_transaction_stubs.rb +43 -0
- data/test/unit/response_stubs/list_transactions_stubs.rb +110 -0
- data/test/unit/response_stubs/purchase_stubs.rb +139 -0
- data/test/unit/response_stubs/redact_payment_method_stubs.rb +54 -0
- data/test/unit/response_stubs/refund_stubs.rb +87 -0
- data/test/unit/response_stubs/retain_payment_method_stubs.rb +85 -0
- data/test/unit/response_stubs/void_stubs.rb +79 -0
- data/test/unit/retain_payment_method_test.rb +44 -0
- data/test/unit/timeout_test.rb +20 -0
- data/test/unit/void_test.rb +96 -0
- metadata +215 -29
- checksums.yaml +0 -15
- data/lib/spreedly/common.rb +0 -44
- data/lib/spreedly/mock.rb +0 -221
- data/lib/spreedly/test_hacks.rb +0 -27
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
NWQ5NjM1NzgyZTBkYWJjY2I1MDM0ZDExMzJiMjIyNTExNjg2ODQ5MA==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NmMyNjUyZTIxNDNiYzAwZjVjNzgwZDRkYzk0MjRkMWNiODkzNmZjOQ==
|
7
|
-
!binary "U0hBNTEy":
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
YmExNTYxMDhlYjdlZGZjZDYxOGE2Y2Q4MTcwNjlmN2M3NWYxYzE2N2FiMGI3
|
10
|
-
YWQ4NjY0OTRhYmViN2IxNWJhNmU4M2ZkYjJlM2I2ODRhMmZlYzM0ZGRhMzU2
|
11
|
-
NzhiMzc4NzUzOGI4MzNhYmIyNmZiYTFkYzgzOTk4YTdjYTcyYTk=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YzZmMDVlYjNhOWQ4NTg3ZTAzNjJjMTE3ZTgzNzViZTYwYTVmYmUzMTZmMzFi
|
14
|
-
NWZlZWY0NjJmYjllZDI5YTJhMzMyMmYzMjdlYTNkZjYxNTk5YTIxM2U5ODkw
|
15
|
-
NmVjMmI3YzU3OGE4NDkwOWRhNzAxYWNjMzhiZjQ2ODc3OTQyMjA=
|
data/lib/spreedly/common.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + '/../../vendor/*'].each do |directory|
|
2
|
-
next unless File.directory?(directory)
|
3
|
-
$LOAD_PATH.unshift File.expand_path(directory + '/lib')
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'uri'
|
7
|
-
require 'bigdecimal'
|
8
|
-
|
9
|
-
require 'spreedly/version'
|
10
|
-
|
11
|
-
module Spreedly
|
12
|
-
# Generates a subscribe url for the given user id and plan.
|
13
|
-
# Options:
|
14
|
-
# :screen_name => a screen name for the user (shows up in the admin UI)
|
15
|
-
# :email => pre-populate the email field
|
16
|
-
# :first_name => pre-populate the first name field
|
17
|
-
# :last_name => pre-populate the last name field
|
18
|
-
def self.subscribe_url(id, plan, options={})
|
19
|
-
%w(screen_name email first_name last_name return_url).each do |option|
|
20
|
-
options[option.to_sym] &&= URI.escape(options[option.to_sym])
|
21
|
-
end
|
22
|
-
|
23
|
-
screen_name = options.delete(:screen_name)
|
24
|
-
params = %w(email first_name last_name return_url).select{|e| options[e.to_sym]}.collect{|e| "#{e}=#{options[e.to_sym]}"}.join('&')
|
25
|
-
|
26
|
-
url = "https://spreedly.com/#{site_name}/subscribers/#{id}/subscribe/#{plan}"
|
27
|
-
url << "/#{screen_name}" if screen_name
|
28
|
-
url << '?' << params unless params == ''
|
29
|
-
|
30
|
-
url
|
31
|
-
end
|
32
|
-
|
33
|
-
# Generates an edit subscriber for the given subscriber token. The
|
34
|
-
# token is returned with the subscriber info (i.e. by
|
35
|
-
# Subscriber.find).
|
36
|
-
def self.edit_subscriber_url(token, return_url = nil)
|
37
|
-
"https://spreedly.com/#{site_name}/subscriber_accounts/#{token}" +
|
38
|
-
if return_url
|
39
|
-
"?return_url=#{URI.escape(return_url)}"
|
40
|
-
else
|
41
|
-
''
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
data/lib/spreedly/mock.rb
DELETED
@@ -1,221 +0,0 @@
|
|
1
|
-
require 'spreedly/common'
|
2
|
-
|
3
|
-
raise "Real Spreedly already required!" if defined?(Spreedly::REAL)
|
4
|
-
|
5
|
-
module Spreedly
|
6
|
-
MOCK = "mock"
|
7
|
-
|
8
|
-
def self.configure(name, token)
|
9
|
-
@site_name = name
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.site_name
|
13
|
-
@site_name
|
14
|
-
end
|
15
|
-
|
16
|
-
class Resource
|
17
|
-
def self.attributes
|
18
|
-
@attributes ||= {}
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.attributes=(value)
|
22
|
-
@attributes = value
|
23
|
-
end
|
24
|
-
|
25
|
-
attr_reader :attributes
|
26
|
-
def initialize(params={})
|
27
|
-
@attributes = self.class.attributes.inject({}){|a,(k,v)| a[k.to_sym] = v.call; a}
|
28
|
-
params.each {|k,v| @attributes[k.to_sym] = v }
|
29
|
-
end
|
30
|
-
|
31
|
-
def id
|
32
|
-
@attributes[:id]
|
33
|
-
end
|
34
|
-
|
35
|
-
def method_missing(method, *args)
|
36
|
-
if method.to_s =~ /\?$/
|
37
|
-
send(method.to_s[0..-2], *args)
|
38
|
-
elsif @attributes.include?(method)
|
39
|
-
@attributes[method]
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Subscriber < Resource
|
47
|
-
self.attributes = {
|
48
|
-
:created_at => proc{Time.now},
|
49
|
-
:token => proc{(rand * 1000).round},
|
50
|
-
:active => proc{false},
|
51
|
-
:store_credit => proc{BigDecimal("0.0")},
|
52
|
-
:active_until => proc{nil},
|
53
|
-
:feature_level => proc{""},
|
54
|
-
:on_trial => proc{false},
|
55
|
-
:recurring => proc{false},
|
56
|
-
:eligible_for_free_trial => proc{false}
|
57
|
-
}
|
58
|
-
|
59
|
-
def self.wipe! # :nodoc: all
|
60
|
-
@subscribers = nil
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.create!(id, *args) # :nodoc: all
|
64
|
-
optional_attrs = args.last.is_a?(::Hash) ? args.pop : {}
|
65
|
-
email, screen_name = args
|
66
|
-
sub = new({:customer_id => id, :email => email, :screen_name => screen_name}.merge(optional_attrs))
|
67
|
-
|
68
|
-
if subscribers[sub.id]
|
69
|
-
raise "Could not create subscriber: already exists."
|
70
|
-
end
|
71
|
-
|
72
|
-
subscribers[sub.id] = sub
|
73
|
-
sub
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.delete!(id)
|
77
|
-
subscribers.delete(id)
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.find(id)
|
81
|
-
subscribers[id]
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.subscribers
|
85
|
-
@subscribers ||= {}
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.all
|
89
|
-
@subscribers.values
|
90
|
-
end
|
91
|
-
|
92
|
-
def initialize(params={})
|
93
|
-
super
|
94
|
-
if !id || id == ''
|
95
|
-
raise "Could not create subscriber: Customer ID can't be blank."
|
96
|
-
end
|
97
|
-
@invoices ||= []
|
98
|
-
end
|
99
|
-
|
100
|
-
def id
|
101
|
-
@attributes[:customer_id]
|
102
|
-
end
|
103
|
-
|
104
|
-
def update(args)
|
105
|
-
args.each_pair do |key, value|
|
106
|
-
if @attributes.has_key?(key)
|
107
|
-
@attributes[key] = value
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def comp(quantity, units, feature_level=nil)
|
113
|
-
raise "Could not comp subscriber: no longer exists." unless self.class.find(id)
|
114
|
-
raise "Could not comp subscriber: validation failed." unless units && quantity
|
115
|
-
current_active_until = (active_until || Time.now)
|
116
|
-
@attributes[:active_until] = case units
|
117
|
-
when 'days'
|
118
|
-
current_active_until + (quantity.to_i * 86400)
|
119
|
-
when 'months'
|
120
|
-
current_active_until + (quantity.to_i * 30 * 86400)
|
121
|
-
end
|
122
|
-
@attributes[:feature_level] = feature_level if feature_level
|
123
|
-
@attributes[:active] = true
|
124
|
-
end
|
125
|
-
|
126
|
-
def activate_free_trial(plan_id)
|
127
|
-
raise "Could not activate free trial for subscriber: validation failed. missing subscription plan id" unless plan_id
|
128
|
-
raise "Could not active free trial for subscriber: subscriber or subscription plan no longer exists." unless self.class.find(id) && SubscriptionPlan.find(plan_id)
|
129
|
-
raise "Could not activate free trial for subscriber: subscription plan either 1) isn't a free trial, 2) the subscriber is not eligible for a free trial, or 3) the subscription plan is not enabled." if (on_trial? and !eligible_for_free_trial?)
|
130
|
-
@attributes[:on_trial] = true
|
131
|
-
plan = SubscriptionPlan.find(plan_id)
|
132
|
-
comp(plan.duration_quantity, plan.duration_units, plan.feature_level)
|
133
|
-
end
|
134
|
-
|
135
|
-
def allow_free_trial
|
136
|
-
@attributes[:eligible_for_free_trial] = true
|
137
|
-
end
|
138
|
-
|
139
|
-
def stop_auto_renew
|
140
|
-
raise "Could not stop auto renew for subscriber: subscriber does not exist." unless self.class.find(id)
|
141
|
-
@attributes[:recurring] = false
|
142
|
-
end
|
143
|
-
|
144
|
-
def subscribe(plan_id, card_number="4222222222222")
|
145
|
-
plan = SubscriptionPlan.find(plan_id)
|
146
|
-
@invoices.unshift(Invoice.new(
|
147
|
-
amount: (@invoices.select{|invoice| invoice.closed?}.size > 0 ? 0 : plan.amount),
|
148
|
-
closed: false
|
149
|
-
))
|
150
|
-
|
151
|
-
return unless card_number == "4222222222222"
|
152
|
-
|
153
|
-
@invoices.first.attributes[:closed] = true
|
154
|
-
@attributes[:recurring] = true
|
155
|
-
comp(plan.duration_quantity, plan.duration_units, plan.feature_level)
|
156
|
-
end
|
157
|
-
|
158
|
-
def add_fee(args)
|
159
|
-
raise "Unprocessable Entity" unless (args.keys & [:amount, :group, :name]).size == 3
|
160
|
-
raise "Unprocessable Entity" unless active?
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
|
164
|
-
def invoices
|
165
|
-
@invoices
|
166
|
-
end
|
167
|
-
|
168
|
-
def last_successful_invoice
|
169
|
-
@invoices.detect{|invoice| invoice.closed?}
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
class Invoice < Resource
|
174
|
-
end
|
175
|
-
|
176
|
-
class SubscriptionPlan < Resource
|
177
|
-
self.attributes = {
|
178
|
-
:plan_type => proc{'regular'},
|
179
|
-
:feature_level => proc{''}
|
180
|
-
}
|
181
|
-
|
182
|
-
def self.all
|
183
|
-
plans.values
|
184
|
-
end
|
185
|
-
|
186
|
-
def self.find(id)
|
187
|
-
plans[id.to_i]
|
188
|
-
end
|
189
|
-
|
190
|
-
def self.plans
|
191
|
-
@plans ||= {
|
192
|
-
1 => new(
|
193
|
-
:id => 1,
|
194
|
-
:name => 'Default mock plan',
|
195
|
-
:duration_quantity => 1,
|
196
|
-
:duration_units => 'days',
|
197
|
-
:amount => 6
|
198
|
-
),
|
199
|
-
2 => new(
|
200
|
-
:id => 2,
|
201
|
-
:name => 'Test Free Trial Plan',
|
202
|
-
:plan_type => 'free_trial',
|
203
|
-
:duration_quantity => 1,
|
204
|
-
:duration_units => 'days',
|
205
|
-
:amount => 11
|
206
|
-
),
|
207
|
-
3 => new(
|
208
|
-
:id => 3,
|
209
|
-
:name => 'Test Regular Plan',
|
210
|
-
:duration_quantity => 1,
|
211
|
-
:duration_units => 'days',
|
212
|
-
:amount => 17
|
213
|
-
)
|
214
|
-
}
|
215
|
-
end
|
216
|
-
|
217
|
-
def trial?
|
218
|
-
(plan_type == "free_trial")
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
data/lib/spreedly/test_hacks.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'mechanize'
|
2
|
-
|
3
|
-
module Spreedly
|
4
|
-
class Subscriber
|
5
|
-
# This method is *strictly* for use when testing, and will
|
6
|
-
# probably only work against a test Spreedly site anyhow.
|
7
|
-
def subscribe(plan_id, card_number="4222222222222")
|
8
|
-
agent = Mechanize.new
|
9
|
-
page = agent.get(Spreedly.subscribe_url(id, plan_id))
|
10
|
-
page = page.forms.first.submit
|
11
|
-
form = page.forms.first
|
12
|
-
form['credit_card[first_name]'] = 'Joe'
|
13
|
-
form['credit_card[last_name]'] = 'Bob'
|
14
|
-
form['subscriber[email]'] = 'joe@example.com'
|
15
|
-
form['credit_card[number]'] = card_number
|
16
|
-
form['credit_card[card_type]'] = 'visa'
|
17
|
-
form['credit_card[verification_value]'] = '234'
|
18
|
-
form['credit_card[month]'] = '1'
|
19
|
-
form['credit_card[year]'] = '2024'
|
20
|
-
page = form.click_button
|
21
|
-
|
22
|
-
if card_number == "4222222222222"
|
23
|
-
raise "Subscription didn't go through" unless page.title == "Thank you!"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|