Class: UsageCredits::CreditSubscriptionPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/usage_credits/models/credit_subscription_plan.rb

Overview

A DSL to define subscription plans that give credits to users on a recurring basis.

The actual credit fulfillment is handled by the PaySubscriptionExtension, which monitors subscription events (creation, renewal, etc) and adds credits to the user’s wallet accordingly.

Defined Under Namespace

Classes: CreditGiver

Constant Summary collapse

MIN_PERIOD =
1.day

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ CreditSubscriptionPlan

Returns a new instance of CreditSubscriptionPlan.



24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 24

def initialize(name)
  @name = name
  @processor_plan_ids = {}  # Store processor-specific plan IDs
  @fulfillment_period = nil
  @credits_per_period = 0
  @signup_bonus_credits = 0
  @trial_credits = 0
  @rollover_enabled = false
  @expire_credits_on_cancel = false
  @credit_expiration_period = nil
  @metadata = {}
end

Instance Attribute Details

#credit_expiration_periodObject (readonly)

Returns the value of attribute credit_expiration_period.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def credit_expiration_period
  @credit_expiration_period
end

#credits_per_periodObject (readonly)

Returns the value of attribute credits_per_period.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def credits_per_period
  @credits_per_period
end

#expire_credits_on_cancelObject (readonly)

Returns the value of attribute expire_credits_on_cancel.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def expire_credits_on_cancel
  @expire_credits_on_cancel
end

#fulfillment_periodObject

Returns the value of attribute fulfillment_period.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def fulfillment_period
  @fulfillment_period
end

#metadataObject (readonly)

Returns the value of attribute metadata.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def 
  @metadata
end

#nameObject (readonly)

Returns the value of attribute name.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def name
  @name
end

#processor_plan_idsObject (readonly)

Returns the value of attribute processor_plan_ids.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def processor_plan_ids
  @processor_plan_ids
end

#rollover_enabledObject (readonly)

Returns the value of attribute rollover_enabled.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def rollover_enabled
  @rollover_enabled
end

#signup_bonus_creditsObject (readonly)

Returns the value of attribute signup_bonus_credits.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def 
  @signup_bonus_credits
end

#trial_creditsObject (readonly)

Returns the value of attribute trial_credits.



12
13
14
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 12

def trial_credits
  @trial_credits
end

Instance Method Details

#base_metadataObject



165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 165

def 
  {
    purchase_type: "credit_subscription",
    subscription_name: name,
    fulfillment_period: fulfillment_period_display,
    credits_per_period: credits_per_period,
    signup_bonus_credits: ,
    trial_credits: trial_credits,
    rollover_enabled: rollover_enabled,
    expire_credits_on_cancel: expire_credits_on_cancel,
    credit_expiration_period: credit_expiration_period&.to_i,
    metadata: 
  }
end

#create_checkout_session(user, success_url:, cancel_url:, processor: :stripe) ⇒ Object

Create a checkout session for this subscription plan

Raises:

  • (ArgumentError)


111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 111

def create_checkout_session(user, success_url:, cancel_url:, processor: :stripe)
  raise ArgumentError, "User must respond to payment_processor" unless user.respond_to?(:payment_processor)
  raise ArgumentError, "No fulfillment period configured for plan: #{name}" unless fulfillment_period

  plan_id = plan_id_for(processor)
  raise ArgumentError, "No #{processor.to_s.titleize} plan ID configured for plan: #{name}" unless plan_id

  case processor
  when :stripe
    create_stripe_checkout_session(user, plan_id, success_url, cancel_url)
  else
    raise ArgumentError, "Unsupported payment processor: #{processor}"
  end
end

#create_stripe_checkout_session(user, plan_id, success_url, cancel_url) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 151

def create_stripe_checkout_session(user, plan_id, success_url, cancel_url)
  user.payment_processor.checkout(
    mode: "subscription",
    line_items: [{
      price: plan_id,
      quantity: 1
    }],
    success_url: success_url,
    cancel_url: cancel_url,
    payment_intent_data: { metadata:  },
    subscription_data: { metadata:  }
  )
end

#expire_after(duration) ⇒ void

This method returns an undefined value.

Configure credit expiration after subscription cancellation

When a subscription is cancelled, you can control what happens to remaining credits:

  1. By default (if this is not called), users keep their credits forever

  2. If called with a duration, credits expire after that grace period

  3. If called with nil/0, credits expire immediately on cancellation

Parameters:

  • duration (ActiveSupport::Duration, nil)

    Grace period before credits expire



77
78
79
80
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 77

def expire_after(duration)
  @expire_credits_on_cancel = true
  @credit_expiration_period = duration
end

#fulfillment_period_displayObject

Helper Methods



143
144
145
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 143

def fulfillment_period_display
  fulfillment_period.is_a?(ActiveSupport::Duration) ? fulfillment_period.inspect : fulfillment_period
end

#gives(amount) ⇒ Object

Set base credits given each period



42
43
44
45
46
47
48
49
50
51
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 42

def gives(amount)
  if amount.is_a?(UsageCredits::Cost::Fixed)
    @credits_per_period = amount.amount
    @fulfillment_period = UsageCredits::PeriodParser.normalize_period(amount.period || 1.month)
    self
  else
    @credits_per_period = amount.to_i
    CreditGiver.new(self)
  end
end

#meta(hash) ⇒ Object

Add custom metadata



83
84
85
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 83

def meta(hash)
  @metadata.merge!(hash)
end

#parsed_fulfillment_periodObject



147
148
149
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 147

def parsed_fulfillment_period
  UsageCredits::PeriodParser.parse_period(@fulfillment_period)
end

#plan_id_for(processor) ⇒ Object

Get the plan ID for a specific processor



97
98
99
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 97

def plan_id_for(processor)
  processor_plan_ids[processor.to_sym]
end

#processor_plan(processor, id) ⇒ Object

Set the processor-specific plan ID



92
93
94
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 92

def processor_plan(processor, id)
  processor_plan_ids[processor.to_sym] = id
end

#signup_bonus(amount) ⇒ Object

One-time signup bonus credits



54
55
56
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 54

def (amount)
  @signup_bonus_credits = amount.to_i
end

#stripe_price(id = nil) ⇒ Object

Shorthand for Stripe price ID



102
103
104
105
106
107
108
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 102

def stripe_price(id = nil)
  if id.nil?
    plan_id_for(:stripe) # getter
  else
    processor_plan(:stripe, id) # setter
  end
end

#trial_includes(amount) ⇒ Object

Credits given during trial period



59
60
61
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 59

def trial_includes(amount)
  @trial_credits = amount.to_i
end

#unused_credits(behavior) ⇒ Object

Configure whether unused credits roll over between periods



64
65
66
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 64

def unused_credits(behavior)
  @rollover_enabled = (behavior == :rollover)
end

#validate!Object

Validation

Raises:

  • (ArgumentError)


130
131
132
133
134
135
136
137
# File 'lib/usage_credits/models/credit_subscription_plan.rb', line 130

def validate!
  raise ArgumentError, "Name can't be blank" if name.blank?
  raise ArgumentError, "Credits per period must be greater than 0" unless credits_per_period.to_i.positive?
  raise ArgumentError, "Fulfillment period must be set" if fulfillment_period.nil?
  raise ArgumentError, "Signup bonus credits must be greater than or equal to 0" if .to_i.negative?
  raise ArgumentError, "Trial credits must be greater than or equal to 0" if trial_credits.to_i.negative?
  true
end