stripe-rails 0.2.0 → 0.2.1
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/Changelog.md +3 -0
- data/README.md +14 -4
- data/lib/generators/stripe/install_generator.rb +1 -0
- data/lib/generators/templates/coupons.rb +39 -0
- data/lib/stripe/configuration_builder.rb +99 -0
- data/lib/stripe/coupons.rb +38 -0
- data/lib/stripe/engine.rb +5 -3
- data/lib/stripe/plans.rb +14 -81
- data/lib/stripe/rails.rb +2 -0
- data/lib/stripe/rails/tasks.rake +6 -2
- data/lib/stripe/rails/version.rb +1 -1
- data/test/coupon_builder_spec.rb +64 -0
- data/test/plan_builder_spec.rb +1 -1
- metadata +7 -2
data/Changelog.md
CHANGED
data/README.md
CHANGED
@@ -71,7 +71,7 @@ this will generate the configuration files containing your plan and coupon defin
|
|
71
71
|
create config/stripe/plans.rb
|
72
72
|
create config/stripe/coupons.rb
|
73
73
|
|
74
|
-
### Configuring your plans
|
74
|
+
### Configuring your plans and coupons
|
75
75
|
|
76
76
|
Use the plan builder to define as many plans as you want in `config/stripe/plans.rb`
|
77
77
|
|
@@ -93,12 +93,22 @@ can refer to them by reference as opposed to an id string.
|
|
93
93
|
Stripe::Plans::SILVER # => 'silver: ACME Silver'
|
94
94
|
Stripe::Plans::GOLD # => 'gold: ACME Gold'
|
95
95
|
|
96
|
-
|
96
|
+
|
97
|
+
Coupons are created in much the same way:
|
98
|
+
|
99
|
+
Stripe.coupon :super_elite_free_vip do |coupon|
|
100
|
+
coupon.duration = 'forever'
|
101
|
+
coupon.percent_off = 100
|
102
|
+
coupon.max_redemptions = 5
|
103
|
+
end
|
104
|
+
|
105
|
+
To upload your plans and coupons onto stripe.com, run:
|
97
106
|
|
98
107
|
rake stripe:prepare
|
99
108
|
|
100
|
-
This will create any plans that do not currently exist, and treat as a NOOP any
|
101
|
-
plans that do, so you can run this command safely as many times as you wish.
|
109
|
+
This will create any plans and coupons that do not currently exist, and treat as a NOOP any
|
110
|
+
plans that do, so you can run this command safely as many times as you wish. Now you can
|
111
|
+
use any of these plans in your application.
|
102
112
|
|
103
113
|
NOTE: You must destroy plans manually from your stripe dashboard.
|
104
114
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# This file contains descriptions of all your statically defined
|
2
|
+
# stripe coupons. You may wish to define unique one-off coupons
|
3
|
+
# elsewhere, but for ones you will use many times, and will be
|
4
|
+
# shared between users, this is a good place.
|
5
|
+
|
6
|
+
# Example
|
7
|
+
# Stripe::Coupons::Gold25 #=> 'gold25'
|
8
|
+
|
9
|
+
# Stripe.coupon :gold25 do |coupon|
|
10
|
+
#
|
11
|
+
# # specify if this coupon is useable 'once', 'forever', or 'repeating
|
12
|
+
# coupon.duration = 'repeating'
|
13
|
+
#
|
14
|
+
# # absolute amount, in cents, to discount
|
15
|
+
# coupon.amount_off = 199
|
16
|
+
#
|
17
|
+
# # what currency to interpret the coupon amount
|
18
|
+
# coupon.currency = 'usd'
|
19
|
+
#
|
20
|
+
# # how long will this coupon last? (only valid for duration of 'repeating')
|
21
|
+
# coupon.duration_in_months = 6
|
22
|
+
#
|
23
|
+
# # percentage off
|
24
|
+
# coupon.percent_off = 25
|
25
|
+
#
|
26
|
+
# UTC timestamp specifying the last time at which the coupon can be redeemed
|
27
|
+
# coupon.reedem_by = (Time.now + 15.days).utc
|
28
|
+
#
|
29
|
+
# # How many times can this coupon be redeemed?
|
30
|
+
# coupon.max_redemptions = 10
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Once you have your coupons defined, you can run
|
34
|
+
#
|
35
|
+
# rake stripe:prepare
|
36
|
+
#
|
37
|
+
# This will export any new coupons to stripe.com so that you can
|
38
|
+
# begin using them in your API calls. Any coupons found that are not in this
|
39
|
+
# file will be left as-is.
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Stripe
|
2
|
+
module ConfigurationBuilder
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class << self
|
7
|
+
def configuration_for(class_id, &block)
|
8
|
+
@_configuration_storage = "@#{class_id.to_s.pluralize}"
|
9
|
+
instance_variable_set(@_configuration_storage, {})
|
10
|
+
configuration_class = Class.new(Stripe::ConfigurationBuilder::Configuration)
|
11
|
+
const_set(:Configuration, configuration_class)
|
12
|
+
configuration_class.class_eval(&block)
|
13
|
+
stripe_class = Stripe.const_get(class_id.to_s.camelize)
|
14
|
+
stripe_configuration_class = self
|
15
|
+
send(:define_method, class_id) do |id, &block|
|
16
|
+
config = configuration_class.new(id, stripe_class, stripe_configuration_class)
|
17
|
+
block.call config
|
18
|
+
config.finalize!
|
19
|
+
end
|
20
|
+
::Stripe.send(:extend, self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def configurations
|
24
|
+
instance_variable_get(@_configuration_storage)
|
25
|
+
end
|
26
|
+
|
27
|
+
def all
|
28
|
+
configurations.values
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](key)
|
32
|
+
configurations[key.to_s]
|
33
|
+
end
|
34
|
+
|
35
|
+
def []=(key, value)
|
36
|
+
configurations[key.to_s] = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def put!
|
40
|
+
all.each(&:put!)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Configuration
|
46
|
+
include ActiveModel::Validations
|
47
|
+
attr_reader :id
|
48
|
+
|
49
|
+
def initialize(id, stripe_class, stripe_configuration_class)
|
50
|
+
@id = id
|
51
|
+
@stripe_class = stripe_class
|
52
|
+
@stripe_configuration_class = stripe_configuration_class
|
53
|
+
end
|
54
|
+
|
55
|
+
def finalize!
|
56
|
+
validate!
|
57
|
+
globalize!
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate!
|
61
|
+
fail Stripe::InvalidConfigurationError, errors if invalid?
|
62
|
+
end
|
63
|
+
|
64
|
+
def globalize!
|
65
|
+
@stripe_configuration_class[@id.to_s] = self
|
66
|
+
@stripe_configuration_class.const_set(@id.to_s.upcase, self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def put!
|
70
|
+
if exists?
|
71
|
+
puts "[EXISTS] - #{@stripe_class}:#{@id}" unless Stripe::Engine.testing
|
72
|
+
else
|
73
|
+
object = @stripe_class.create({:id => @id}.merge create_options)
|
74
|
+
puts "[CREATE] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
@id.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
def exists?
|
83
|
+
!!@stripe_class.retrieve(to_s)
|
84
|
+
rescue Stripe::InvalidRequestError
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class InvalidConfigurationError < StandardError
|
91
|
+
attr_reader :errors
|
92
|
+
|
93
|
+
def initialize(errors)
|
94
|
+
super errors.messages
|
95
|
+
@errors = errors
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Stripe
|
2
|
+
module Coupons
|
3
|
+
include ConfigurationBuilder
|
4
|
+
|
5
|
+
configuration_for :coupon do
|
6
|
+
attr_accessor :duration, :amount_off, :currency, :duration_in_months, :max_redemptions, :percent_off, :redeem_by
|
7
|
+
|
8
|
+
validates_presence_of :id, :duration
|
9
|
+
validates_presence_of :duration_in_months, :if => :repeating?
|
10
|
+
validates_inclusion_of :duration, :in => %w(forever once repeating), :message => "'%{value}' is not one of 'forever', 'once' or 'repeating'"
|
11
|
+
validates_inclusion_of :percent_off, in: 1..100, unless: ->(coupon) {coupon.percent_off.nil?}
|
12
|
+
validates_numericality_of :percent_off, :greater_than => 0
|
13
|
+
validates_numericality_of :duration_in_months, :greater_than => 0, :if => :repeating?
|
14
|
+
validates_numericality_of :max_redemptions, :greater_than => 0
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
super
|
18
|
+
@currency = 'usd'
|
19
|
+
end
|
20
|
+
|
21
|
+
def repeating?
|
22
|
+
duration == 'repeating'
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_options
|
26
|
+
{
|
27
|
+
:duration => duration,
|
28
|
+
:percent_off => percent_off,
|
29
|
+
:amount_off => amount_off,
|
30
|
+
:currency => currency,
|
31
|
+
:duration_in_months => duration_in_months,
|
32
|
+
:max_redemptions => max_redemptions,
|
33
|
+
:redeem_by => redeem_by
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/stripe/engine.rb
CHANGED
@@ -38,9 +38,11 @@ environment file directly.
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
initializer 'stripe.
|
42
|
-
|
43
|
-
|
41
|
+
initializer 'stripe.plans_and_coupons' do |app|
|
42
|
+
for configuration in %w(plans coupons)
|
43
|
+
path = app.root.join("config/stripe/#{configuration}.rb")
|
44
|
+
load path if path.exist?
|
45
|
+
end
|
44
46
|
end
|
45
47
|
|
46
48
|
rake_tasks do
|
data/lib/stripe/plans.rb
CHANGED
@@ -1,98 +1,31 @@
|
|
1
1
|
module Stripe
|
2
2
|
module Plans
|
3
|
-
|
3
|
+
include ConfigurationBuilder
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.[](key)
|
10
|
-
@plans[key.to_s]
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.[]=(key, value)
|
14
|
-
@plans[key.to_s] = value
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.put!
|
18
|
-
all.each do |plan|
|
19
|
-
plan.put!
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def plan(id)
|
24
|
-
config = Configuration.new(id)
|
25
|
-
yield config
|
26
|
-
config.finalize!
|
27
|
-
end
|
28
|
-
|
29
|
-
class Configuration
|
30
|
-
include ActiveModel::Validations
|
31
|
-
attr_reader :id, :currency
|
5
|
+
configuration_for :plan do
|
6
|
+
attr_reader :currency
|
32
7
|
attr_accessor :name, :amount, :interval, :interval_count, :trial_period_days
|
33
8
|
|
34
9
|
validates_presence_of :id, :name, :amount
|
35
10
|
validates_inclusion_of :interval, :in => %w(month year), :message => "'%{value}' is not one of 'month' or 'year'"
|
36
11
|
|
37
|
-
def initialize(
|
38
|
-
|
12
|
+
def initialize(*args)
|
13
|
+
super(*args)
|
39
14
|
@currency = 'usd'
|
40
15
|
@interval_count = 1
|
41
16
|
@trial_period_days = 0
|
42
17
|
end
|
43
18
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
def globalize!
|
54
|
-
Stripe::Plans[@id.to_s] = self
|
55
|
-
Stripe::Plans.const_set(@id.to_s.upcase, self)
|
56
|
-
end
|
57
|
-
|
58
|
-
def put!
|
59
|
-
if exists?
|
60
|
-
puts "[EXISTS] - #{@id}" unless Stripe::Engine.testing
|
61
|
-
else
|
62
|
-
plan = Stripe::Plan.create(
|
63
|
-
:id => @id,
|
64
|
-
:currency => @currency,
|
65
|
-
:name => @name,
|
66
|
-
:amount => @amount,
|
67
|
-
:interval => @interval,
|
68
|
-
:interval_count => @interval_count,
|
69
|
-
:trial_period_days => @trial_period_days
|
70
|
-
)
|
71
|
-
puts "[CREATE] - #{plan}" unless Stripe::Engine.testing
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def to_s
|
76
|
-
@id.to_s
|
77
|
-
end
|
78
|
-
|
79
|
-
def exists?
|
80
|
-
!!Stripe::Plan.retrieve("#{@id}")
|
81
|
-
rescue Stripe::InvalidRequestError
|
82
|
-
false
|
19
|
+
def create_options
|
20
|
+
{
|
21
|
+
:currency => @currency,
|
22
|
+
:name => @name,
|
23
|
+
:amount => @amount,
|
24
|
+
:interval => @interval,
|
25
|
+
:interval_count => @interval_count,
|
26
|
+
:trial_period_days => @trial_period_days
|
27
|
+
}
|
83
28
|
end
|
84
29
|
end
|
85
|
-
|
86
|
-
class InvalidPlanError < StandardError
|
87
|
-
attr_reader :errors
|
88
|
-
|
89
|
-
def initialize(errors)
|
90
|
-
super errors.messages
|
91
|
-
@errors = errors
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
30
|
end
|
97
|
-
extend Plans
|
98
31
|
end
|
data/lib/stripe/rails.rb
CHANGED
data/lib/stripe/rails/tasks.rake
CHANGED
@@ -15,6 +15,10 @@ namespace :stripe do
|
|
15
15
|
Stripe::Plans.put!
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
task 'coupons:prepare' => 'environment' do
|
19
|
+
Stripe::Coupons.put!
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "create all plans and coupons defined in config/stripe/{plans|coupons}.rb"
|
23
|
+
task 'prepare' => ['plans:prepare', 'coupons:prepare']
|
20
24
|
end
|
data/lib/stripe/rails/version.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'building plans' do
|
5
|
+
describe 'simply' do
|
6
|
+
before do
|
7
|
+
@now = Time.now.utc
|
8
|
+
Stripe.coupon(:gold25) do |coupon|
|
9
|
+
coupon.duration = 'repeating'
|
10
|
+
coupon.duration_in_months = 10
|
11
|
+
coupon.amount_off = 100
|
12
|
+
coupon.currency = 'USD'
|
13
|
+
coupon.max_redemptions = 3
|
14
|
+
coupon.percent_off = 25
|
15
|
+
coupon.redeem_by = @now
|
16
|
+
end
|
17
|
+
end
|
18
|
+
after {Stripe::Coupons.send(:remove_const, :GOLD25)}
|
19
|
+
|
20
|
+
it 'is accessible via hash lookup (symbol/string agnostic)' do
|
21
|
+
Stripe::Coupons[:gold25].must_equal Stripe::Coupons::GOLD25
|
22
|
+
Stripe::Coupons['gold25'].must_equal Stripe::Coupons::GOLD25
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'uploading' do
|
26
|
+
describe 'when none exists on stripe.com' do
|
27
|
+
before do
|
28
|
+
Stripe::Coupon.stubs(:retrieve).raises(Stripe::InvalidRequestError.new("not found", "id"))
|
29
|
+
end
|
30
|
+
it 'creates the plan online' do
|
31
|
+
Stripe::Coupon.expects(:create).with(
|
32
|
+
:id => :gold25,
|
33
|
+
:duration => 'repeating',
|
34
|
+
:duration_in_months => 10,
|
35
|
+
:amount_off => 100,
|
36
|
+
:currency => 'USD',
|
37
|
+
:max_redemptions => 3,
|
38
|
+
:percent_off => 25,
|
39
|
+
:redeem_by => @now
|
40
|
+
)
|
41
|
+
Stripe::Coupons.put!
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
describe 'when it is already present on stripe.com' do
|
46
|
+
before do
|
47
|
+
Stripe::Coupon.stubs(:retrieve).returns(Stripe::Coupon.construct_from({
|
48
|
+
:id => :gold25,
|
49
|
+
}))
|
50
|
+
end
|
51
|
+
it 'is a no-op' do
|
52
|
+
Stripe::Coupon.expects(:create).never
|
53
|
+
Stripe::Coupons.put!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'with missing mandatory values' do
|
60
|
+
it 'raises an exception after configuring it' do
|
61
|
+
proc {Stripe.plan(:bad) {}}.must_raise Stripe::InvalidConfigurationError
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/test/plan_builder_spec.rb
CHANGED
@@ -65,7 +65,7 @@ describe 'building plans' do
|
|
65
65
|
|
66
66
|
describe 'with missing mandatory values' do
|
67
67
|
it 'raises an exception after configuring it' do
|
68
|
-
proc {Stripe.plan(:bad) {}}.must_raise Stripe::
|
68
|
+
proc {Stripe.plan(:bad) {}}.must_raise Stripe::InvalidConfigurationError
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stripe-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -113,10 +113,13 @@ files:
|
|
113
113
|
- app/views/stripe/_js.html.erb
|
114
114
|
- config/routes.rb
|
115
115
|
- lib/generators/stripe/install_generator.rb
|
116
|
+
- lib/generators/templates/coupons.rb
|
116
117
|
- lib/generators/templates/plans.rb
|
117
118
|
- lib/stripe-rails.rb
|
118
119
|
- lib/stripe/callbacks.rb
|
119
120
|
- lib/stripe/callbacks/builder.rb
|
121
|
+
- lib/stripe/configuration_builder.rb
|
122
|
+
- lib/stripe/coupons.rb
|
120
123
|
- lib/stripe/engine.rb
|
121
124
|
- lib/stripe/plans.rb
|
122
125
|
- lib/stripe/rails.rb
|
@@ -125,6 +128,7 @@ files:
|
|
125
128
|
- stripe-rails.gemspec
|
126
129
|
- test/.DS_Store
|
127
130
|
- test/callbacks_spec.rb
|
131
|
+
- test/coupon_builder_spec.rb
|
128
132
|
- test/dummy/README.rdoc
|
129
133
|
- test/dummy/Rakefile
|
130
134
|
- test/dummy/app/assets/javascripts/application.js
|
@@ -189,6 +193,7 @@ summary: A gem to integrate stripe into your rails app
|
|
189
193
|
test_files:
|
190
194
|
- test/.DS_Store
|
191
195
|
- test/callbacks_spec.rb
|
196
|
+
- test/coupon_builder_spec.rb
|
192
197
|
- test/dummy/README.rdoc
|
193
198
|
- test/dummy/Rakefile
|
194
199
|
- test/dummy/app/assets/javascripts/application.js
|