tansengming/stripe-rails

View on GitHub
lib/stripe/prices.rb

Summary

Maintainability
A
1 hr
Test Coverage
A
96%
module Stripe
module Prices
include ConfigurationBuilder
VALID_TIME_UNITS = %i(day week month year)
 
configuration_for :price do
attr_reader :lookup_key
attr_accessor :active,
:billing_scheme,
:constant_name,
:currency,
:metadata,
:name,
:nickname,
:object,
:product_id,
:recurring,
:statement_descriptor,
:tax_behavior,
:tiers,
:tiers_mode,
:transform_quantity,
:type,
:unit_amount
 
validates_presence_of :id, :currency
validates_presence_of :unit_amount, unless: ->(p) { p.billing_scheme == 'tiered' }
validates_absence_of :transform_quantity, if: ->(p) { p.billing_scheme == 'tiered' }
validates_presence_of :tiers_mode, :tiers, if: ->(p) { p.billing_scheme == 'tiered' }
 
validates_numericality_of :recurring_interval_count, allow_nil: true
 
validates_inclusion_of :recurring_interval,
in: VALID_TIME_UNITS.collect(&:to_s),
message: "'%{value}' is not one of #{VALID_TIME_UNITS.to_sentence(last_word_connector: ', or ')}",
if: ->(p) { p.recurring.present? }
 
validates :statement_descriptor, length: { maximum: 22 }
 
validates :active, inclusion: { in: [true, false] }, allow_nil: true
validates :billing_scheme, inclusion: { in: %w{ per_unit tiered } }, allow_nil: true
validates :recurring_aggregate_usage, inclusion: { in: %w{ sum last_during_period last_ever max } }, allow_nil: true
validates :recurring_usage_type, inclusion: { in: %w{ metered licensed } }, allow_nil: true
validates :tax_behavior, inclusion: { in: %w{ inclusive exclusive unspecified } }, allow_nil: true
validates :tiers_mode, inclusion: { in: %w{ graduated volume } }, allow_nil: true
 
validate :name_or_product_id
validate :recurring_aggregate_usage_must_be_metered, if: ->(p) { p.recurring_aggregate_usage.present? }
validate :recurring_interval_count_maximum, if: ->(p) { p.recurring_interval_count.present? }
validate :valid_constant_name, unless: ->(p) { p.constant_name.nil? }
 
# validations for when using tiered billing
validate :tiers_must_be_array, if: ->(p) { p.tiers.present? }
validate :billing_scheme_must_be_tiered, if: ->(p) { p.tiers.present? }
validate :validate_tiers, if: ->(p) { p.billing_scheme == 'tiered' }
 
def initialize(*args)
super(*args)
@currency = 'usd'
@lookup_key = @id.to_s
@recurring = (recurring || {}).symbolize_keys
end
 
# We're overriding a handful of the Configuration methods so that
# we find and create by lookup_key instead of by ID. The ID is assigned
# by stripe and out of our control
Method `put!` has a Cognitive Complexity of 11 (exceeds 5 allowed). Consider refactoring.
def put!
if exists?
puts "[EXISTS] - #{@stripe_class}:#{@id}:#{stripe_id}" unless Stripe::Engine.testing
else
object = @stripe_class.create({:lookup_key => @lookup_key}.merge compact_create_options)
puts "[CREATE] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
end
 
# You can't delete prices, but you can transfer the lookup key to a new price
def reset!
object = @stripe_class.create(reset_options)
puts "[RESET] - #{@stripe_class}:#{object}" unless Stripe::Engine.testing
end
 
def exists?
stripe_object.presence
rescue Stripe::InvalidRequestError
false
end
 
def stripe_object
@stripe_class.list({lookup_keys: [@lookup_key]}).data.first.presence || nil
rescue Stripe::InvalidRequestError
nil
end
 
def stripe_id
@stripe_id ||= stripe_object.try(:id)
end
 
def recurring_interval
recurring[:interval]
end
 
def recurring_aggregate_usage
recurring[:aggregate_usage]
end
 
def recurring_usage_type
recurring[:usage_type]
end
 
def recurring_interval_count
recurring[:interval_count]
end
 
private
def recurring_aggregate_usage_must_be_metered
errors.add(:recurring_aggregate_usage, 'recurring[:usage_type] must be metered') unless (recurring_usage_type == 'metered')
end
 
Method `recurring_interval_count_maximum` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
def recurring_interval_count_maximum
time_unit = recurring_interval.to_sym
 
return unless VALID_TIME_UNITS.include?(time_unit) && recurring_interval_count.respond_to?(time_unit)
too_long = recurring_interval_count.send(time_unit) > 1.year
 
errors.add(:recurring_interval_count, 'recurring[:interval_count] Maximum is one year (1 year, 12 months, or 52 weeks') if too_long
end
 
def name_or_product_id
errors.add(:base, 'must have a product_id or a name') unless (@product_id.present? ^ @name.present?)
end
 
def billing_scheme_must_be_tiered
errors.add(:billing_scheme, 'must be set to `tiered` when specifying `tiers`') unless billing_scheme == 'tiered'
end
 
def tiers_must_be_array
errors.add(:tiers, 'must be an Array') unless tiers.is_a?(Array)
end
 
def billing_tiers
@billing_tiers = tiers.map { |t| Stripe::Plans::BillingTier.new(t) } if tiers
end
 
def validate_tiers
billing_tiers.all?(&:valid?)
end
 
module ConstTester; end
def valid_constant_name
ConstTester.const_set(constant_name.to_s.upcase, constant_name)
ConstTester.send(:remove_const, constant_name.to_s.upcase.to_sym)
rescue NameError
errors.add(:constant_name, 'is not a valid Ruby constant name.')
end
 
def reset_options
existing_object = stripe_object
# Lookup and set the existing product ID if unset
@product_id ||= existing_object.product if existing_object.present?
 
{ transfer_lookup_key: existing_object.present? }.merge(compact_create_options)
end
 
def create_options
{
currency: currency,
unit_amount: unit_amount,
active: active,
metadata: metadata,
nickname: nickname.presence || @lookup_key,
recurring: recurring.compact,
tiers: tiers ? tiers.map(&:to_h) : nil,
tiers_mode: tiers_mode,
billing_scheme: billing_scheme,
lookup_key: @lookup_key,
tax_behavior: tax_behavior,
transform_quantity: transform_quantity,
}.merge(product_options).compact
end
 
def product_options
if product_id.present?
{ product: product_id }
else
{
product_data: { name: name, statement_descriptor: statement_descriptor }
}
end
end
end
end
end