Class: CoreMerchant::Subscription

Inherits:
ActiveRecord::Base
  • Object
show all
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

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`.



101
102
103
# File 'lib/core_merchant/subscription.rb', line 101

def cancel(reason:, at_period_end: true)
  CoreMerchant.subscription_manager.cancel_subscription(self, reason: reason, at_period_end: at_period_end)
end

#days_remaining_in_current_periodObject

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.



120
121
122
# File 'lib/core_merchant/subscription.rb', line 120

def days_remaining_in_current_period
  (current_period_end.to_date - Time.current.to_date).to_i
end

#days_remaining_in_grace_periodObject



138
139
140
141
142
# File 'lib/core_merchant/subscription.rb', line 138

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

Returns:

  • (Boolean)


148
149
150
# File 'lib/core_merchant/subscription.rb', line 148

def due_for_renewal?
  renewable? && current_period_end <= Time.current
end

#expired_or_canceled?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/core_merchant/subscription.rb', line 152

def expired_or_canceled?
  expired? || canceled?
end

#grace_periodObject

Returns the number of days as a grace period for past-due subscriptions. By default, this is 3 days.



126
127
128
# File 'lib/core_merchant/subscription.rb', line 126

def grace_period
  3.days
end

#grace_period_end_dateObject



130
131
132
# File 'lib/core_merchant/subscription.rb', line 130

def grace_period_end_date
  current_period_end + grace_period
end

#grace_period_exceeded?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/core_merchant/subscription.rb', line 144

def grace_period_exceeded?
  due_for_renewal? && Time.current > grace_period_end_date
end

#in_grace_period?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/core_merchant/subscription.rb', line 134

def in_grace_period?
  due_for_renewal? && Time.current <= grace_period_end_date
end

#ongoing?Boolean

Returns:

  • (Boolean)


160
161
162
# File 'lib/core_merchant/subscription.rb', line 160

def ongoing?
  active? || trial? || past_due? || processing_renewal? || processing_payment? || pending_cancellation?
end

#processing?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/core_merchant/subscription.rb', line 156

def processing?
  processing_renewal? || processing_payment?
end

#renewable?Boolean

Returns:

  • (Boolean)


164
165
166
# File 'lib/core_merchant/subscription.rb', line 164

def renewable?
  active? || trial? || past_due? || processing?
end

#startObject

Starts the subscription. Sets the current period start and end dates based on the plan’s duration.



91
92
93
# File 'lib/core_merchant/subscription.rb', line 91

def start
  CoreMerchant.subscription_manager.start_subscription(self)
end

#start_new_periodObject

Starts a new period for the subscription. This is called by SubscriptionManager when a subscription renewal is successful.



107
108
109
110
111
112
113
114
115
# File 'lib/core_merchant/subscription.rb', line 107

def start_new_period
  new_period_start = current_period_end || start_date
  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