Class: CoreMerchant::Subscription
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- CoreMerchant::Subscription
- Includes:
- Concerns::SubscriptionEventAssociation, Concerns::SubscriptionNotifications, Concerns::SubscriptionStateMachine
- Defined in:
- lib/core_merchant/subscription.rb
Overview
Represents a subscription in CoreMerchant. This class manages the lifecycle of a customer’s subscription to a specific plan.
**Subscriptions can transition through various statuses**:
-
‘pending`: Subscription created but not yet started
-
‘trial`: In a trial period
-
‘active`: Currently active and paid
-
‘past_due`: Payment failed but in grace period
-
‘pending_cancellation`: Will be canceled at period end
-
‘processing_renewal`: Renewal in progress
-
‘processing_payment`: Payment processing
-
‘canceled`: Canceled by user or due to payment failure
-
‘expired`: Subscription period ended
-
‘paused`: Temporarily halted, not yet implemented
-
‘pending_change`: Plan change scheduled for next renewal, not yet implemented
**Key features**:
-
Supports immediate and end-of-period cancellations
-
Allows plan changes, effective immediately or at next renewal
-
Handles subscription pausing and resuming
-
Manages trial periods
-
Supports variable pricing for renewals
Attributes:
-
‘customer`: Polymorphic association to the customer
-
‘subscription_plan`: The current plan for this subscription
-
‘status`: Current status of the subscription (see enum definition)
-
‘start_date`: When the subscription started
-
‘end_date`: When the subscription ended (or will end)
-
‘trial_end_date`: End date of the trial period (if applicable)
-
‘canceled_at`: When the subscription was canceled
-
‘current_period_start`: Start of the current billing period
-
‘current_period_end`: End of the current billing period
-
‘pause_start_date`: When the subscription was paused
-
‘pause_end_date`: When the paused subscription will resume
-
‘current_period_price_cents`: Price for the current period
-
‘next_renewal_price_cents`: Price for the next renewal (if different from plan)
-
‘cancellation_reason`: Reason for cancellation (if applicable)
Usage:
```ruby
subscription = CoreMerchant::Subscription.create(customer: user, subscription_plan: plan, status: :active)
subscription.start
subscription.cancel(reason: "Too expensive", at_period_end: true)
```
Constant Summary
Constants included from Concerns::SubscriptionEventAssociation
Concerns::SubscriptionEventAssociation::EVENT_TYPES
Constants included from Concerns::SubscriptionStateMachine
Concerns::SubscriptionStateMachine::POSSIBLE_TRANSITIONS
Instance Method Summary collapse
-
#cancel(reason:, at_period_end: true) ⇒ Object
Cancels the subscription.
-
#days_remaining_in_current_period ⇒ Object
Returns the days remaining in the current period.
- #days_remaining_in_grace_period ⇒ Object
- #due_for_renewal? ⇒ Boolean
- #expired_or_canceled? ⇒ Boolean
-
#grace_period ⇒ Object
Returns the number of days as a grace period for past-due subscriptions.
- #grace_period_end_date ⇒ Object
- #grace_period_exceeded? ⇒ Boolean
- #in_grace_period? ⇒ Boolean
- #ongoing? ⇒ Boolean
- #processing? ⇒ Boolean
- #renewable? ⇒ Boolean
-
#start ⇒ Object
Starts the subscription.
-
#start_new_period ⇒ Object
Starts a new period for the subscription.
Instance Method Details
#cancel(reason:, at_period_end: true) ⇒ Object
Cancels the subscription. Parameters:
-
‘reason`: Reason for cancellation
-
‘at_period_end`: If true, the subscription will be canceled at the end of the current period. Otherwise, the subscription will be canceled immediately. Default is `true`.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/core_merchant/subscription.rb', line 112 def cancel(reason:, at_period_end: true) transaction do if at_period_end transition_to_pending_cancellation! else transition_to_canceled! end update!( canceled_at: at_period_end ? current_period_end : Time.current, cancellation_reason: reason ) end notify_subscription_manager(:canceled, reason: reason, immediate: !at_period_end) cancellation_events.create!(reason: reason, at_period_end: at_period_end) end |
#days_remaining_in_current_period ⇒ Object
Returns the days remaining in the current period. Use to show the user how many days are left before the next renewal or to refund pro-rated amounts for early cancellations.
144 145 146 |
# File 'lib/core_merchant/subscription.rb', line 144 def days_remaining_in_current_period (current_period_end.to_date - Time.current.to_date).to_i end |
#days_remaining_in_grace_period ⇒ Object
162 163 164 165 166 |
# File 'lib/core_merchant/subscription.rb', line 162 def days_remaining_in_grace_period return 0 unless due_for_renewal? (grace_period_end_date.to_date - Time.current.to_date).to_i end |
#due_for_renewal? ⇒ Boolean
172 173 174 |
# File 'lib/core_merchant/subscription.rb', line 172 def due_for_renewal? renewable? && current_period_end <= Time.current end |
#expired_or_canceled? ⇒ Boolean
176 177 178 |
# File 'lib/core_merchant/subscription.rb', line 176 def expired_or_canceled? expired? || canceled? end |
#grace_period ⇒ Object
Returns the number of days as a grace period for past-due subscriptions. By default, this is 3 days.
150 151 152 |
# File 'lib/core_merchant/subscription.rb', line 150 def grace_period 3.days end |
#grace_period_end_date ⇒ Object
154 155 156 |
# File 'lib/core_merchant/subscription.rb', line 154 def grace_period_end_date current_period_end + grace_period end |
#grace_period_exceeded? ⇒ Boolean
168 169 170 |
# File 'lib/core_merchant/subscription.rb', line 168 def grace_period_exceeded? due_for_renewal? && Time.current > grace_period_end_date end |
#in_grace_period? ⇒ Boolean
158 159 160 |
# File 'lib/core_merchant/subscription.rb', line 158 def in_grace_period? due_for_renewal? && Time.current <= grace_period_end_date end |
#ongoing? ⇒ Boolean
184 185 186 |
# File 'lib/core_merchant/subscription.rb', line 184 def ongoing? active? || trial? || past_due? || processing_renewal? || processing_payment? || pending_cancellation? end |
#processing? ⇒ Boolean
180 181 182 |
# File 'lib/core_merchant/subscription.rb', line 180 def processing? processing_renewal? || processing_payment? end |
#renewable? ⇒ Boolean
188 189 190 |
# File 'lib/core_merchant/subscription.rb', line 188 def renewable? active? || trial? || past_due? || processing? end |
#start ⇒ Object
Starts the subscription. Sets the current period start and end dates based on the plan’s duration.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/core_merchant/subscription.rb', line 91 def start new_period_start = start_date new_period_end = new_period_start + subscription_plan.duration_in_date transaction do transition_to_active! update!( current_period_start: new_period_start.to_date, current_period_end: new_period_end.to_date ) end notify_subscription_manager(:started) end |
#start_new_period ⇒ Object
Starts a new period for the subscription. This is called by SubscriptionManager when a subscription renewal is successful.
131 132 133 134 135 136 137 138 139 |
# File 'lib/core_merchant/subscription.rb', line 131 def start_new_period new_period_start = current_period_end new_period_end = new_period_start + subscription_plan.duration_in_date update!( current_period_start: new_period_start.to_date, current_period_end: new_period_end.to_date ) end |