stripe-rails 2.0.0.pre → 2.3.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.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +12 -1
- data/.github/workflows/ruby.yml +4 -1
- data/Changelog.md +28 -1
- data/README.md +44 -6
- data/Rakefile +0 -0
- data/config/routes.rb +0 -1
- data/gemfiles/rails60.gemfile +19 -0
- data/lib/generators/stripe/install_generator.rb +1 -0
- data/lib/generators/templates/prices.rb +46 -0
- data/lib/stripe/callbacks.rb +2 -0
- data/lib/stripe/coupons.rb +2 -1
- data/lib/stripe/engine.rb +5 -5
- data/lib/stripe/prices.rb +189 -0
- data/lib/stripe/rails.rb +1 -0
- data/lib/stripe/rails/tasks.rake +11 -2
- data/lib/stripe/rails/version.rb +1 -1
- data/test/coupon_builder_spec.rb +1 -0
- data/test/dummy/config/stripe/prices.rb +59 -0
- data/test/dummy_apis_controller_spec.rb +1 -1
- data/test/events_controller_spec.rb +22 -3
- data/test/fixtures/stripe_prices.json +7 -0
- data/test/plan_builder_spec.rb +1 -1
- data/test/price_builder_spec.rb +594 -0
- data/test/stripe_initializers_spec.rb +47 -4
- metadata +17 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e1778aaa249a48297d0f7c95ff5d893d3dcda21107d608ab6b262cf500b2a70
|
4
|
+
data.tar.gz: babf50def492764aba044d993a2acd8338161485dc96ae28eae8bb88dc0bc15b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a656096e3c1fa2988387a31b535dd879418466661e805a3351bfd6b0728c67bd9880146359d840fcffb18b50dfa940f207fb4916eede34c38685469b9060b8e
|
7
|
+
data.tar.gz: 812f9988e7bbc66f2eff2c433cf918c05732799bf5ecd5d9b161fa954ee7ec38a9a9715ac43332bd27782978b1c1142fcb531cd3ef053a9d61162a8822e969d5
|
@@ -1,4 +1,15 @@
|
|
1
1
|
<!--
|
2
|
-
|
2
|
+
Thanks a bunch for helping out with the project!
|
3
|
+
|
4
|
+
Please remember to,
|
5
|
+
|
6
|
+
1. Add tests if they do not exist, fix em if they are breaking
|
7
|
+
2. Fix any issues that are stopping Code Climate from passing
|
8
|
+
2. Add a short description of the feature and tag yourself on Changelog.md
|
9
|
+
|
10
|
+
That's it!
|
11
|
+
|
12
|
+
Please give me ~1 week to get back to you.
|
13
|
+
|
3
14
|
If you'd like to receive occasional updates, sign up for our newsletter at http://tinyletter.com/stripe-rails
|
4
15
|
-->
|
data/.github/workflows/ruby.yml
CHANGED
@@ -15,7 +15,7 @@ jobs:
|
|
15
15
|
strategy:
|
16
16
|
matrix:
|
17
17
|
ruby: [2.5.x, 2.6.x, 2.7.x]
|
18
|
-
gemfile: [Gemfile, gemfiles/
|
18
|
+
gemfile: [Gemfile, gemfiles/rails60.gemfile, gemfiles/rails52.gemfile, gemfiles/rails51.gemfile]
|
19
19
|
steps:
|
20
20
|
- uses: actions/checkout@v1
|
21
21
|
- name: Set up Ruby
|
@@ -33,6 +33,9 @@ jobs:
|
|
33
33
|
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
34
34
|
RUBY_VERSION: ${{ matrix.ruby }}
|
35
35
|
run: |
|
36
|
+
if [ $RUBY_VERSION == "2.5.x" ] ;
|
37
|
+
then gem install bundler
|
38
|
+
fi
|
36
39
|
bundle install --jobs 4 --retry 3
|
37
40
|
bundle exec rake
|
38
41
|
if [ `basename $BUNDLE_GEMFILE` == "Gemfile" ] && [ $RUBY_VERSION == "2.7.x" ] && [ ! -z ${CC_TEST_REPORTER_ID} ] ;
|
data/Changelog.md
CHANGED
@@ -1,6 +1,33 @@
|
|
1
1
|
## Unreleased
|
2
2
|
|
3
|
-
-
|
3
|
+
-
|
4
|
+
|
5
|
+
## 2.3.0 (2021-03-08)
|
6
|
+
|
7
|
+
- Adds testing for Rails 6.0
|
8
|
+
- Add `name` attribute to coupons. Thanks @ZilvinasKucinskas!
|
9
|
+
|
10
|
+
## 2.2.1 (2020-12-22)
|
11
|
+
|
12
|
+
- Add payment_intent.requires_action callback, thanks @VadimLeader.
|
13
|
+
|
14
|
+
## 2.2.0 (2020-12-06)
|
15
|
+
|
16
|
+
- Add Prices as a configuration object. Thanks @jamesprior!
|
17
|
+
|
18
|
+
## 2.1.0 (2020-10-18)
|
19
|
+
|
20
|
+
- Added option to ignore missing API key and don't show any warning. Thanks @ndbroadbent!
|
21
|
+
- Handle passing nil to signing_secret= and add tests. Thanks @martron!
|
22
|
+
|
23
|
+
## 2.0.0 (2020-09-18)
|
24
|
+
|
25
|
+
- Everything from on the 2.0.0.pre release
|
26
|
+
- includes changes from the 1.10.2 release
|
27
|
+
|
28
|
+
## 1.10.2 (2020-09-18)
|
29
|
+
|
30
|
+
- adds missing callback `invoice.paid`. Thanks @SyborgStudios.
|
4
31
|
|
5
32
|
## 2.0.0.pre (2020-05-29)
|
6
33
|
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ This gem can help your rails application integrate with Stripe in the following
|
|
9
9
|
|
10
10
|
* manage stripe configurations in a single place.
|
11
11
|
* makes stripe.js available from the asset pipeline.
|
12
|
-
* manage plans and coupons from within your app.
|
12
|
+
* manage product, prices, plans and coupons from within your app.
|
13
13
|
* painlessly receive and validate webhooks from stripe.
|
14
14
|
|
15
15
|
[📫 Sign up for the Newsletter](http://tinyletter.com/stripe-rails) to receive occasional updates.
|
@@ -137,6 +137,14 @@ you prefer to environment variables, you can also set `STRIPE_PUBLISHABLE_KEY`:
|
|
137
137
|
export STRIPE_PUBLISHABLE_KEY=pk_test_XXXYYYZZZ
|
138
138
|
```
|
139
139
|
|
140
|
+
If no API key is provided, `stripe-rails` will show a warning: "No stripe.com API key was configured ...". You can silence this warning by setting the `ignore_missing_secret_key` option to `true`:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# config/environments/production.rb
|
144
|
+
# ...
|
145
|
+
config.stripe.ignore_missing_secret_key = true
|
146
|
+
```
|
147
|
+
|
140
148
|
### Manually set your API version (optional)
|
141
149
|
|
142
150
|
If you need to test a new API version in development, you can override the version number manually.
|
@@ -162,6 +170,7 @@ this will generate the configuration files containing your plan and coupon defin
|
|
162
170
|
```console
|
163
171
|
create config/stripe/products.rb
|
164
172
|
create config/stripe/plans.rb
|
173
|
+
create config/stripe/prices.rb
|
165
174
|
create config/stripe/coupons.rb
|
166
175
|
```
|
167
176
|
|
@@ -259,17 +268,46 @@ Stripe.product :primo do |product|
|
|
259
268
|
end
|
260
269
|
```
|
261
270
|
|
262
|
-
|
271
|
+
And Prices:
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
Stripe.price :bronze do |price|
|
275
|
+
# Use an existing product id to prevent a new product from
|
276
|
+
# getting created
|
277
|
+
price.product_id = Stripe::Products::PRIMO.id
|
278
|
+
price.billing_scheme = 'tiered'
|
279
|
+
price.recurring = {
|
280
|
+
interval: 'month',
|
281
|
+
usage_type: 'metered'
|
282
|
+
}
|
283
|
+
|
284
|
+
# Use graduated pricing tiers
|
285
|
+
# ref: https://stripe.com/docs/api/prices/object#price_object-tiers
|
286
|
+
price.tiers = [
|
287
|
+
{
|
288
|
+
unit_amount: 1500,
|
289
|
+
up_to: 10
|
290
|
+
},
|
291
|
+
{
|
292
|
+
unit_amount: 1000,
|
293
|
+
up_to: 'inf'
|
294
|
+
}
|
295
|
+
]
|
296
|
+
price.tiers_mode = 'graduated'
|
297
|
+
end
|
298
|
+
````
|
299
|
+
|
300
|
+
To upload your plans, products, prices and coupons onto stripe.com, run:
|
263
301
|
|
264
302
|
```sh
|
265
303
|
rake stripe:prepare
|
266
304
|
```
|
267
305
|
|
268
|
-
This will create any plans and coupons that do not currently exist, and treat as a NOOP any
|
269
|
-
|
270
|
-
use any of these
|
306
|
+
This will create any plans, products, prices and coupons that do not currently exist, and treat as a NOOP any
|
307
|
+
objects that already exist, so you can run this command safely as many times as you wish. Now you can
|
308
|
+
use any of these objects in your application.
|
271
309
|
|
272
|
-
NOTE: You must destroy plans manually from your stripe dashboard.
|
310
|
+
NOTE: You must destroy plans and prices manually from your stripe dashboard.
|
273
311
|
|
274
312
|
## Stripe Elements
|
275
313
|
|
data/Rakefile
CHANGED
File without changes
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'rails', '~> 6.0.0'
|
4
|
+
|
5
|
+
gem 'rake'
|
6
|
+
gem 'responders'
|
7
|
+
gem 'stripe'
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem 'mocha'
|
11
|
+
gem 'simplecov', require: false
|
12
|
+
gem 'stripe-ruby-mock'
|
13
|
+
gem 'webmock'
|
14
|
+
# Required for system tests
|
15
|
+
gem 'capybara'
|
16
|
+
gem 'puma'
|
17
|
+
gem 'selenium-webdriver'
|
18
|
+
gem 'webdrivers'
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This file contains descriptions of all your stripe prices
|
2
|
+
|
3
|
+
# Example
|
4
|
+
# Stripe::Prices::LITE.lookup_key #=> 'lite'
|
5
|
+
|
6
|
+
# Prices will have a stripe generated id. The lookup_key will match the
|
7
|
+
# configuration below. You can fetch the ID or object from stripe:
|
8
|
+
#
|
9
|
+
# Stripe::Prices::LITE.stripe_id #=> 'price_0000sdfs2qfsdf'
|
10
|
+
# Stripe::Prices::LITE.stripe_object #=> #<Stripe::Price:0x3584 id=price_0000sdfs2qfsdf>...
|
11
|
+
|
12
|
+
# Prices are not deletable via the API, the `reset!` method will instead
|
13
|
+
# create a new price and transfer the lookup key to the new price.
|
14
|
+
|
15
|
+
# Stripe.price :lite do |price|
|
16
|
+
# # Prices may belong to a product, this will create a product along with the price
|
17
|
+
# price.name = 'Acme as a service LITE'
|
18
|
+
|
19
|
+
# # You can also specify an existing product ID
|
20
|
+
# # price.product_id = Stripe::Products::PRIMO.id
|
21
|
+
#
|
22
|
+
# # amount in cents. This is 6.99
|
23
|
+
# price.unit_amount = 699
|
24
|
+
#
|
25
|
+
# # currency to use for the price (default 'usd')
|
26
|
+
# price.currency = 'usd'
|
27
|
+
#
|
28
|
+
# price.recurring = {
|
29
|
+
# # interval must be either 'day', 'week', 'month' or 'year'
|
30
|
+
# interval: 'month',
|
31
|
+
# # only bill once every three months (default 1)
|
32
|
+
# interval_count: 3,
|
33
|
+
# # Must be either 'metered' or 'licensed'
|
34
|
+
# usage_type: 'metered',
|
35
|
+
# # Specifies a usage aggregation strategy for metered usage
|
36
|
+
# aggregate_usage: 'sum'
|
37
|
+
# }
|
38
|
+
#
|
39
|
+
# end
|
40
|
+
|
41
|
+
# Once you have your prices defined, you can run
|
42
|
+
#
|
43
|
+
# rake stripe:prepare
|
44
|
+
#
|
45
|
+
# This will export any new prices to stripe.com so that you can
|
46
|
+
# begin using them in your API calls.
|
data/lib/stripe/callbacks.rb
CHANGED
@@ -47,6 +47,7 @@ module Stripe
|
|
47
47
|
callback 'invoice.created'
|
48
48
|
callback 'invoice.finalized'
|
49
49
|
callback 'invoice.marked_uncollectible'
|
50
|
+
callback 'invoice.paid'
|
50
51
|
callback 'invoice.payment_action_required'
|
51
52
|
callback 'invoice.payment_failed'
|
52
53
|
callback 'invoice.payment_succeeded'
|
@@ -67,6 +68,7 @@ module Stripe
|
|
67
68
|
callback 'payment_intent.created'
|
68
69
|
callback 'payment_intent.payment_failed'
|
69
70
|
callback 'payment_intent.processing'
|
71
|
+
callback 'payment_intent.requires_action'
|
70
72
|
callback 'payment_intent.succeeded'
|
71
73
|
callback 'payment_method.attached'
|
72
74
|
callback 'payment_method.card_automatically_updated'
|
data/lib/stripe/coupons.rb
CHANGED
@@ -3,7 +3,7 @@ module Stripe
|
|
3
3
|
include ConfigurationBuilder
|
4
4
|
|
5
5
|
configuration_for :coupon do
|
6
|
-
attr_accessor :duration, :amount_off, :currency, :duration_in_months, :max_redemptions, :percent_off, :redeem_by
|
6
|
+
attr_accessor :name, :duration, :amount_off, :currency, :duration_in_months, :max_redemptions, :percent_off, :redeem_by
|
7
7
|
|
8
8
|
validates_presence_of :id, :duration
|
9
9
|
validates_presence_of :duration_in_months, :if => :repeating?
|
@@ -25,6 +25,7 @@ module Stripe
|
|
25
25
|
|
26
26
|
def create_options
|
27
27
|
{
|
28
|
+
:name => name,
|
28
29
|
:duration => duration,
|
29
30
|
:percent_off => percent_off,
|
30
31
|
:amount_off => amount_off,
|
data/lib/stripe/engine.rb
CHANGED
@@ -8,12 +8,12 @@ module Stripe
|
|
8
8
|
attr_accessor :testing
|
9
9
|
end
|
10
10
|
|
11
|
-
stripe_config = config.stripe = Struct.new(:api_base, :api_version, :secret_key, :verify_ssl_certs, :signing_secret, :signing_secrets, :publishable_key, :endpoint, :debug_js, :auto_mount, :eager_load, :open_timeout, :read_timeout) do
|
11
|
+
stripe_config = config.stripe = Struct.new(:api_base, :api_version, :secret_key, :ignore_missing_secret_key, :verify_ssl_certs, :signing_secret, :signing_secrets, :publishable_key, :endpoint, :debug_js, :auto_mount, :eager_load, :open_timeout, :read_timeout) do
|
12
12
|
# for backwards compatibility treat signing_secret as an alias for signing_secrets
|
13
13
|
def signing_secret=(value)
|
14
|
-
self.signing_secrets = Array(value)
|
14
|
+
self.signing_secrets = value.nil? ? value : Array(value)
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def signing_secret
|
18
18
|
self.signing_secrets && self.signing_secrets.first
|
19
19
|
end
|
@@ -43,7 +43,7 @@ module Stripe
|
|
43
43
|
end
|
44
44
|
secret_key = app.config.stripe.secret_key
|
45
45
|
Stripe.api_key = secret_key unless secret_key.nil?
|
46
|
-
$stderr.puts <<-MSG unless Stripe.api_key
|
46
|
+
$stderr.puts <<-MSG unless Stripe.api_key || app.config.stripe.ignore_missing_secret_key
|
47
47
|
No stripe.com API key was configured for environment #{::Rails.env}! this application will be
|
48
48
|
unable to interact with stripe.com. You can set your API key with either the environment
|
49
49
|
variable `STRIPE_SECRET_KEY` (recommended) or by setting `config.stripe.secret_key` in your
|
@@ -89,7 +89,7 @@ environment file directly.
|
|
89
89
|
end
|
90
90
|
|
91
91
|
initializer 'stripe.plans_and_coupons' do |app|
|
92
|
-
for configuration in %w(products plans coupons)
|
92
|
+
for configuration in %w(products plans coupons prices)
|
93
93
|
path = app.root.join("config/stripe/#{configuration}.rb")
|
94
94
|
load path if path.exist?
|
95
95
|
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
module Stripe
|
2
|
+
module Prices
|
3
|
+
include ConfigurationBuilder
|
4
|
+
VALID_TIME_UNITS = %i(day week month year)
|
5
|
+
|
6
|
+
configuration_for :price do
|
7
|
+
attr_reader :lookup_key
|
8
|
+
attr_accessor :active,
|
9
|
+
:billing_scheme,
|
10
|
+
:constant_name,
|
11
|
+
:currency,
|
12
|
+
:metadata,
|
13
|
+
:name,
|
14
|
+
:nickname,
|
15
|
+
:object,
|
16
|
+
:product_id,
|
17
|
+
:recurring,
|
18
|
+
:statement_descriptor,
|
19
|
+
:tiers,
|
20
|
+
:tiers_mode,
|
21
|
+
:transform_quantity,
|
22
|
+
:type,
|
23
|
+
:unit_amount
|
24
|
+
|
25
|
+
validates_presence_of :id, :currency
|
26
|
+
validates_presence_of :unit_amount, unless: ->(p) { p.billing_scheme == 'tiered' }
|
27
|
+
validates_absence_of :transform_quantity, if: ->(p) { p.billing_scheme == 'tiered' }
|
28
|
+
validates_presence_of :tiers_mode, :tiers, if: ->(p) { p.billing_scheme == 'tiered' }
|
29
|
+
|
30
|
+
validates_numericality_of :recurring_interval_count, allow_nil: true
|
31
|
+
|
32
|
+
validates_inclusion_of :recurring_interval,
|
33
|
+
in: VALID_TIME_UNITS.collect(&:to_s),
|
34
|
+
message: "'%{value}' is not one of #{VALID_TIME_UNITS.to_sentence(last_word_connector: ', or ')}",
|
35
|
+
if: ->(p) { p.recurring.present? }
|
36
|
+
|
37
|
+
validates :statement_descriptor, length: { maximum: 22 }
|
38
|
+
|
39
|
+
validates :active, inclusion: { in: [true, false] }, allow_nil: true
|
40
|
+
validates :billing_scheme, inclusion: { in: %w{ per_unit tiered } }, allow_nil: true
|
41
|
+
validates :recurring_aggregate_usage, inclusion: { in: %w{ sum last_during_period last_ever max } }, allow_nil: true
|
42
|
+
validates :recurring_usage_type, inclusion: { in: %w{ metered licensed } }, allow_nil: true
|
43
|
+
validates :tiers_mode, inclusion: { in: %w{ graduated volume } }, allow_nil: true
|
44
|
+
|
45
|
+
validate :name_or_product_id
|
46
|
+
validate :recurring_aggregate_usage_must_be_metered, if: ->(p) { p.recurring_aggregate_usage.present? }
|
47
|
+
validate :recurring_interval_count_maximum, if: ->(p) { p.recurring_interval_count.present? }
|
48
|
+
validate :valid_constant_name, unless: ->(p) { p.constant_name.nil? }
|
49
|
+
|
50
|
+
# validations for when using tiered billing
|
51
|
+
validate :tiers_must_be_array, if: ->(p) { p.tiers.present? }
|
52
|
+
validate :billing_scheme_must_be_tiered, if: ->(p) { p.tiers.present? }
|
53
|
+
validate :validate_tiers, if: ->(p) { p.billing_scheme == 'tiered' }
|
54
|
+
|
55
|
+
def initialize(*args)
|
56
|
+
super(*args)
|
57
|
+
@currency = 'usd'
|
58
|
+
@lookup_key = @id.to_s
|
59
|
+
@recurring = (recurring || {}).symbolize_keys
|
60
|
+
end
|
61
|
+
|
62
|
+
# We're overriding a handful of the Configuration methods so that
|
63
|
+
# we find and create by lookup_key instead of by ID. The ID is assigned
|
64
|
+
# by stripe and out of our control
|
65
|
+
def put!
|
66
|
+
if exists?
|
67
|
+
puts "[EXISTS] - #{@stripe_class}:#{@id}:#{stripe_id}" unless Stripe::Engine.testing
|
68
|
+
else
|
69
|
+
object = @stripe_class.create({:lookup_key => @lookup_key}.merge compact_create_options)
|
70
|
+
puts "[CREATE] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# You can't delete prices, but you can transfer the lookup key to a new price
|
75
|
+
def reset!
|
76
|
+
object = @stripe_class.create(reset_options)
|
77
|
+
puts "[RESET] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
|
78
|
+
end
|
79
|
+
|
80
|
+
def exists?
|
81
|
+
stripe_object.presence
|
82
|
+
rescue Stripe::InvalidRequestError
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
def stripe_object
|
87
|
+
@stripe_class.list({lookup_keys: [@lookup_key]}).data.first.presence || nil
|
88
|
+
rescue Stripe::InvalidRequestError
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def stripe_id
|
93
|
+
@stripe_id ||= stripe_object.try(:id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def recurring_interval
|
97
|
+
recurring[:interval]
|
98
|
+
end
|
99
|
+
|
100
|
+
def recurring_aggregate_usage
|
101
|
+
recurring[:aggregate_usage]
|
102
|
+
end
|
103
|
+
|
104
|
+
def recurring_usage_type
|
105
|
+
recurring[:usage_type]
|
106
|
+
end
|
107
|
+
|
108
|
+
def recurring_interval_count
|
109
|
+
recurring[:interval_count]
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def recurring_aggregate_usage_must_be_metered
|
114
|
+
errors.add(:recurring_aggregate_usage, 'recurring[:usage_type] must be metered') unless (recurring_usage_type == 'metered')
|
115
|
+
end
|
116
|
+
|
117
|
+
def recurring_interval_count_maximum
|
118
|
+
time_unit = recurring_interval.to_sym
|
119
|
+
|
120
|
+
return unless VALID_TIME_UNITS.include?(time_unit) && recurring_interval_count.respond_to?(time_unit)
|
121
|
+
too_long = recurring_interval_count.send(time_unit) > 1.year
|
122
|
+
|
123
|
+
errors.add(:recurring_interval_count, 'recurring[:interval_count] Maximum is one year (1 year, 12 months, or 52 weeks') if too_long
|
124
|
+
end
|
125
|
+
|
126
|
+
def name_or_product_id
|
127
|
+
errors.add(:base, 'must have a product_id or a name') unless (@product_id.present? ^ @name.present?)
|
128
|
+
end
|
129
|
+
|
130
|
+
def billing_scheme_must_be_tiered
|
131
|
+
errors.add(:billing_scheme, 'must be set to `tiered` when specifying `tiers`') unless billing_scheme == 'tiered'
|
132
|
+
end
|
133
|
+
|
134
|
+
def tiers_must_be_array
|
135
|
+
errors.add(:tiers, 'must be an Array') unless tiers.is_a?(Array)
|
136
|
+
end
|
137
|
+
|
138
|
+
def billing_tiers
|
139
|
+
@billing_tiers = tiers.map { |t| Stripe::Plans::BillingTier.new(t) } if tiers
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate_tiers
|
143
|
+
billing_tiers.all?(&:valid?)
|
144
|
+
end
|
145
|
+
|
146
|
+
module ConstTester; end
|
147
|
+
def valid_constant_name
|
148
|
+
ConstTester.const_set(constant_name.to_s.upcase, constant_name)
|
149
|
+
ConstTester.send(:remove_const, constant_name.to_s.upcase.to_sym)
|
150
|
+
rescue NameError
|
151
|
+
errors.add(:constant_name, 'is not a valid Ruby constant name.')
|
152
|
+
end
|
153
|
+
|
154
|
+
def reset_options
|
155
|
+
existing_object = stripe_object
|
156
|
+
# Lookup and set the existing product ID if unset
|
157
|
+
@product_id ||= existing_object.product if existing_object.present?
|
158
|
+
|
159
|
+
{ transfer_lookup_key: existing_object.present? }.merge(compact_create_options)
|
160
|
+
end
|
161
|
+
|
162
|
+
def create_options
|
163
|
+
{
|
164
|
+
currency: currency,
|
165
|
+
unit_amount: unit_amount,
|
166
|
+
active: active,
|
167
|
+
metadata: metadata,
|
168
|
+
nickname: nickname.presence || @lookup_key,
|
169
|
+
recurring: recurring.compact,
|
170
|
+
tiers: tiers ? tiers.map(&:to_h) : nil,
|
171
|
+
tiers_mode: tiers_mode,
|
172
|
+
billing_scheme: billing_scheme,
|
173
|
+
lookup_key: @lookup_key,
|
174
|
+
transform_quantity: transform_quantity,
|
175
|
+
}.merge(product_options).compact
|
176
|
+
end
|
177
|
+
|
178
|
+
def product_options
|
179
|
+
if product_id.present?
|
180
|
+
{ product: product_id }
|
181
|
+
else
|
182
|
+
{
|
183
|
+
product_data: { name: name, statement_descriptor: statement_descriptor }
|
184
|
+
}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|