core_merchant 0.6.2 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fceb2084b15e74e2aebe360df8cade628ffd930afd2ef689a6779c3f59f5f70a
4
- data.tar.gz: 96368a736ce980a109bfd9d863b56cbec0c814111edd4839874738d13cc6c9e3
3
+ metadata.gz: 2bd7b45750d9491ddf78c12b41e73a1d79d8bc6fa5cdff69df22f536b1765e42
4
+ data.tar.gz: 056e3021e60c7c5edb4a301dae4a29b6184ad3889b55c81e226dccfcfcb00020
5
5
  SHA512:
6
- metadata.gz: 643e829909744d948af7a5270ebdd0f3be2dfa7f3d540da9f0fd728fc9d052731719d6ec6f93cfc73d7357f25c3bbd50a8fd32046b0c2a354058fc611d34e8ac
7
- data.tar.gz: a2ecdd77b9628e2dee8b67b422f0d4fa2f1a62214c28bc9bb1ae72338986272b03bfce1a193969a3fbbc6e440e45ac041c8dd2c637de1fa0d7f796244f6b6daf
6
+ metadata.gz: f916fe4fb53d28a9fbb61c8d7306825cb96d82ceeb559a6aeea1373c83e5401c785c0400dd3b41f0d5f9a4c321f0cc91c85eddead5808d546c7fb09d6b671aa7
7
+ data.tar.gz: 218935fdbb73a113262735e5b4a0290d7942121b167932ad00a5ead4c1a75ef69d8f18dfffca5fea14aafbbb8bf79f6cb940042f15f0989a59ce1e6c14a58208
data/.rubocop.yml CHANGED
@@ -12,6 +12,9 @@ Style/StringLiteralsInInterpolation:
12
12
  Layout/LineLength:
13
13
  Max: 120
14
14
 
15
+ Metrics/MethodLength:
16
+ Max: 16
17
+
15
18
  # Disable frozen string literals for lib/generators/**
16
19
  FrozenStringLiteralComment:
17
20
  Exclude:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- core_merchant (0.6.0)
4
+ core_merchant (0.8.0)
5
5
  activesupport (~> 7.0)
6
6
  rails (~> 7.0)
7
7
 
@@ -99,6 +99,8 @@ GEM
99
99
  diff-lcs (1.5.1)
100
100
  drb (2.2.1)
101
101
  erubi (1.13.0)
102
+ factory_bot (6.4.6)
103
+ activesupport (>= 5.0.0)
102
104
  generator_spec (0.9.5)
103
105
  activesupport (>= 3.0.0)
104
106
  railties (>= 3.0.0)
@@ -251,6 +253,7 @@ PLATFORMS
251
253
  DEPENDENCIES
252
254
  core_merchant!
253
255
  database_cleaner
256
+ factory_bot
254
257
  generator_spec (~> 0.9.4)
255
258
  railties (~> 7.0)
256
259
  rake (~> 13.0)
data/README.md CHANGED
@@ -9,8 +9,8 @@ CoreMerchant is a library for customer, product, and subscription management in
9
9
  - [X] Add customer behavior
10
10
  - [X] Add initializer generator
11
11
  - [X] Add SubscriptionPlan model
12
- - [ ] Add Subscription model
13
- - [ ] Add subscription management service
12
+ - [X] Add Subscription model
13
+ - [ ] Implement subscription manager and callbacks
14
14
  - [ ] Add sidekiq jobs for subscription management
15
15
  - [ ] Add Invoice model
16
16
  - [ ] Add billing and invoicing service
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
37
37
  spec.add_dependency "rails", "~> 7.0"
38
38
 
39
39
  spec.add_development_dependency "database_cleaner"
40
+ spec.add_development_dependency "factory_bot"
40
41
  spec.add_development_dependency "generator_spec", "~> 0.9.4"
41
42
  spec.add_development_dependency "railties", "~> 7.0"
42
43
  spec.add_development_dependency "rspec-rails", "~> 5.0"
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CoreMerchant
4
+ module Concerns
5
+ # Includes logic for renewal processing, grace period handling, and expiration checking.
6
+ module SubscriptionManagerRenewals
7
+ extend ActiveSupport::Concern
8
+
9
+ included do # rubocop:disable Metrics/BlockLength
10
+ def check_renewals
11
+ Subscription.find_each do |subscription|
12
+ process_for_renewal(subscription) if subscription.due_for_renewal?
13
+ end
14
+ end
15
+
16
+ def process_for_renewal(subscription)
17
+ return unless subscription.transition_to_processing_renewal
18
+
19
+ notify(subscription, :due_for_renewal)
20
+ end
21
+
22
+ def no_payment_needed_for_renewal(subscription)
23
+ return unless subscription.transition_to_active
24
+
25
+ notify(subscription, :renewed)
26
+ end
27
+
28
+ def processing_payment_for_renewal(subscription)
29
+ return unless subscription.transition_to_processing_payment
30
+
31
+ notify(subscription, :renewal_payment_processing)
32
+ end
33
+
34
+ def payment_successful_for_renewal(subscription)
35
+ return unless subscription.transition_to_active
36
+
37
+ notify(subscription, :renewed)
38
+ end
39
+
40
+ def payment_failed_for_renewal(subscription)
41
+ is_in_grace_period = subscription.in_grace_period?
42
+ if is_in_grace_period
43
+ subscription.transition_to_past_due
44
+ notify(subscription, :grace_period_started, days_remaining: subscription.days_remaining_in_grace_period)
45
+ else
46
+ subscription.transition_to_expired
47
+ notify(subscription, :expired)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module CoreMerchant
6
+ module Concerns
7
+ # Includes logic for notifying the SubscriptionManager when a subscription is created or destroyed,
8
+ # as well as providing a hook for custom notification logic.
9
+ module SubscriptionNotifications
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ # Notify SubscriptionManager on creation and destruction.
14
+ after_create { notify_subscription_manager(:created) }
15
+ after_destroy { notify_subscription_manager(:destroyed) }
16
+
17
+ def notify_subscription_manager(event, **options)
18
+ CoreMerchant.subscription_manager.notify(self, event, **options)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,26 +9,29 @@ module CoreMerchant
9
9
  # Adds state machine logic to a subscription.
10
10
  # This module defines the possible states and transitions for a subscription.
11
11
  # Possible transitions:
12
- # - `pending` -> `active`, `trial`
13
- # - `trial` -> `active`, `pending_cancellation`, `canceled`
14
- # - `active` -> `pending_cancellation`, `canceled`, `expired`
12
+ # - `pending` -> `active`, `trial`, `processing_renewal`
13
+ # - `trial` -> `processing_renewal`, `active`, `canceled`, `pending_cancellation`, `expired`
14
+ # - `active` -> `pending_cancellation`, `canceled`, `expired`, `processing_renewal`
15
+ # - `past_due` -> `processing_renewal`
15
16
  # - `pending_cancellation` -> `canceled`, `expired`
16
- # - `canceled` -> `pending`, `active`
17
- # - `expired` -> `pending`, `active`
17
+ # - `processing_renewal` -> `processing_payment`, `active`, `expired`, `past_due`
18
+ # - `processing_payment` -> `active`, `expired`, `canceled`, `past_due`
19
+ # - `canceled` -> `pending`, `processing_renewal`
20
+ # - `expired` -> `pending`, `processing_renewal`
18
21
  module SubscriptionStateMachine
19
22
  extend ActiveSupport::Concern
20
23
 
21
24
  # List of possible transitions in the form of { to_state: [from_states] }
22
25
  POSSIBLE_TRANSITIONS = {
23
26
  pending: %i[canceled expired],
24
- trial: %i[pending],
25
- active: %i[pending trial canceled expired],
27
+ trial: [:pending],
28
+ active: %i[pending trial processing_renewal processing_payment],
29
+ past_due: %i[active processing_renewal processing_payment],
26
30
  pending_cancellation: %i[active trial],
27
- canceled: %i[active pending_cancellation trial],
28
- expired: %i[active pending_cancellation canceled trial],
29
- past_due: [],
30
- paused: [],
31
- pending_change: []
31
+ processing_renewal: %i[pending trial active past_due canceled expired],
32
+ processing_payment: [:processing_renewal],
33
+ canceled: %i[trial active pending_cancellation processing_payment],
34
+ expired: %i[trial active pending_cancellation processing_renewal processing_payment]
32
35
  }.freeze
33
36
 
34
37
  included do
@@ -6,18 +6,19 @@ module CoreMerchant
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- end
9
+ has_many :subscriptions, class_name: "CoreMerchant::Subscription", as: :customer, dependent: :destroy
10
10
 
11
- def core_merchant_customer_id
12
- id
13
- end
11
+ def core_merchant_customer_id
12
+ id
13
+ end
14
14
 
15
- def core_merchant_customer_email
16
- email
17
- end
15
+ def core_merchant_customer_email
16
+ email
17
+ end
18
18
 
19
- def core_merchant_customer_name
20
- name if respond_to?(:name)
19
+ def core_merchant_customer_name
20
+ name if respond_to?(:name)
21
+ end
21
22
  end
22
23
  end
23
24
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "core_merchant/concerns/subscription_state_machine"
4
+ require "core_merchant/concerns/subscription_notifications"
4
5
 
5
6
  module CoreMerchant
6
7
  # Represents a subscription in CoreMerchant.
@@ -10,8 +11,10 @@ module CoreMerchant
10
11
  # - `pending`: Subscription created but not yet started
11
12
  # - `trial`: In a trial period
12
13
  # - `active`: Currently active and paid
13
- # - `past_due`: Payment failed but in grace period, not yet implemented
14
+ # - `past_due`: Payment failed but in grace period
14
15
  # - `pending_cancellation`: Will be canceled at period end
16
+ # - `processing_renewal`: Renewal in progress
17
+ # - `processing_payment`: Payment processing
15
18
  # - `canceled`: Canceled by user or due to payment failure
16
19
  # - `expired`: Subscription period ended
17
20
  # - `paused`: Temporarily halted, not yet implemented
@@ -51,6 +54,7 @@ module CoreMerchant
51
54
  # ```
52
55
  class Subscription < ActiveRecord::Base
53
56
  include CoreMerchant::Concerns::SubscriptionStateMachine
57
+ include CoreMerchant::Concerns::SubscriptionNotifications
54
58
 
55
59
  self.table_name = "core_merchant_subscriptions"
56
60
 
@@ -61,12 +65,14 @@ module CoreMerchant
61
65
  pending: 0,
62
66
  trial: 1,
63
67
  active: 2,
64
- past_due: 3, # Logic not yet implemented
65
- pending_cancellation: 4,
66
- canceled: 5,
67
- expired: 6,
68
- paused: 7, # Logic not yet implemented
69
- pending_change: 8 # Logic not yet implemented
68
+ past_due: 10,
69
+ pending_cancellation: 11,
70
+ processing_renewal: 20,
71
+ processing_payment: 21,
72
+ canceled: 30,
73
+ expired: 31,
74
+ paused: 40, # Logic not yet implemented
75
+ pending_change: 50 # Logic not yet implemented
70
76
  }
71
77
 
72
78
  validates :customer, :subscription_plan, :status, :start_date, presence: true
@@ -74,37 +80,83 @@ module CoreMerchant
74
80
  validate :end_date_after_start_date, if: :end_date
75
81
  validate :canceled_at_with_reason, if: :canceled_at
76
82
 
83
+ # Starts the subscription.
84
+ # Sets the current period start and end dates based on the plan's duration.
77
85
  def start
78
86
  new_period_start = start_date
79
87
  new_period_end = new_period_start + subscription_plan.duration_in_date
80
88
 
81
- transition_to_active!
82
- update!(
83
- current_period_start: new_period_start,
84
- current_period_end: new_period_end
85
- )
89
+ transaction do
90
+ transition_to_active!
91
+ update!(
92
+ current_period_start: new_period_start,
93
+ current_period_end: new_period_end
94
+ )
95
+ end
96
+
97
+ notify_subscription_manager(:started)
86
98
  end
87
99
 
88
- def cancel(reason:, at_period_end:)
89
- if at_period_end
90
- transition_to_pending_cancellation!
91
- else
92
- transition_to_canceled!
100
+ # Cancels the subscription.
101
+ # Parameters:
102
+ # - `reason`: Reason for cancellation
103
+ # - `at_period_end`: If true, the subscription will be canceled at the end of the current period.
104
+ # Otherwise, the subscription will be canceled immediately.
105
+ # Default is `true`.
106
+ def cancel(reason:, at_period_end: true)
107
+ transaction do
108
+ if at_period_end
109
+ transition_to_pending_cancellation!
110
+ else
111
+ transition_to_canceled!
112
+ end
113
+ update!(
114
+ canceled_at: at_period_end ? current_period_end : Time.current,
115
+ cancellation_reason: reason
116
+ )
93
117
  end
94
- update!(
95
- canceled_at: at_period_end ? current_period_end : Time.current,
96
- cancellation_reason: reason
97
- )
118
+
119
+ notify_subscription_manager(:canceled, reason: reason, immediate: !at_period_end)
98
120
  end
99
121
 
100
- private
122
+ # Returns the days remaining in the current period.
123
+ # Use to show the user how many days are left before the next renewal or
124
+ # to refund pro-rated amounts for early cancellations.
125
+ def days_remaining_in_current_period
126
+ (current_period_end.to_date - Time.current.to_date).to_i
127
+ end
128
+
129
+ # Returns the number of days as a grace period for past-due subscriptions.
130
+ # By default, this is 3 days.
131
+ def grace_period
132
+ 3.days
133
+ end
134
+
135
+ def grace_period_end_date
136
+ current_period_end + grace_period
137
+ end
101
138
 
102
- def set_period_dates
103
- self.start_date ||= Time.current
104
- self.current_period_start ||= start_date
105
- self.current_period_end ||= start_date + subscription_plan.duration_in_date
139
+ def in_grace_period?
140
+ due_for_renewal? && Time.current <= grace_period_end_date
106
141
  end
107
142
 
143
+ def days_remaining_in_grace_period
144
+ return 0 unless due_for_renewal?
145
+
146
+ (grace_period_end_date.to_date - Time.current.to_date).to_i
147
+ end
148
+
149
+ def grace_period_exceeded?
150
+ due_for_renewal? && Time.current > grace_period_end_date
151
+ end
152
+
153
+ def due_for_renewal?
154
+ (active? || trial? || past_due? || processing_renewal? || processing_payment?) &&
155
+ current_period_end <= Time.current
156
+ end
157
+
158
+ private
159
+
108
160
  def end_date_after_start_date
109
161
  errors.add(:end_date, "must be after the `start date") if end_date <= start_date
110
162
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CoreMerchant
4
+ # Include this module in your application to listen for subscription events.
5
+ module SubscriptionListener
6
+ extend ActiveSupport::Concern
7
+
8
+ included do # rubocop:disable Metrics/BlockLength
9
+ def on_test_event_received
10
+ puts "Test event received by CoreMerchant::SubscriptionListener. Override this method in your application."
11
+ end
12
+
13
+ # rubocop:disable Metrics/LineLength
14
+
15
+ def on_subscription_created(subscription)
16
+ puts "Subscription (#{subscription.id}) created. Override #{__method__} in your application to handle this event."
17
+ end
18
+
19
+ def on_subscription_destroyed(subscription)
20
+ puts "Subscription (#{subscription.id}) destroyed. Override #{__method__} in your application to handle this event."
21
+ end
22
+
23
+ def on_subscription_started(subscription)
24
+ puts "Subscription (#{subscription.id}) started. Override #{__method__} in your application to handle this event."
25
+ end
26
+
27
+ def on_subscription_canceled(subscription, reason:, immediate:)
28
+ puts "Subscription (#{subscription.id}) canceled with reason '#{reason}' and immediate=#{immediate}. Override #{__method__} in your application to handle this event."
29
+ end
30
+
31
+ def on_subscription_due_for_renewal(subscription)
32
+ puts "Subscription (#{subscription.id}) is due for renewal. Override #{__method__} in your application to handle this event."
33
+ end
34
+
35
+ def on_subscription_grace_period_(subscription, days_remaining)
36
+ puts "Subscription (#{subscription.id}) has entered a grace period with #{days_remaining} days remaining. Override #{__method__} in your application to handle this event."
37
+ end
38
+
39
+ def on_subscription_renewed(subscription)
40
+ puts "Subscription (#{subscription.id}) renewed. Override #{__method__} in your application to handle this event."
41
+ end
42
+
43
+ def on_subscription_renewal_payment_processing(subscription)
44
+ puts "Subscription (#{subscription.id}) renewal payment processing. Override #{__method__} in your application to handle this event."
45
+ end
46
+
47
+ def on_subscription_expired(subscription)
48
+ puts "Subscription (#{subscription.id}) expired. Override #{__method__} in your application to handle this event."
49
+ end
50
+
51
+ # rubocop:enable Metrics/LineLength
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "core_merchant/concerns/subscription_manager_renewals"
4
+
5
+ module CoreMerchant
6
+ # Manages subscriptions in CoreMerchant.
7
+ # This class is responsible for notifying listeners when subscription events occur.
8
+ # Attributes:
9
+ # - `listeners` - An array of listeners that will be notified when subscription events occur.
10
+ class SubscriptionManager
11
+ include Concerns::SubscriptionManagerRenewals
12
+
13
+ attr_reader :listeners
14
+
15
+ def initialize
16
+ @listeners = []
17
+ end
18
+
19
+ def check_subscriptions
20
+ check_renewals
21
+ end
22
+
23
+ def add_listener(listener)
24
+ @listeners << listener
25
+ end
26
+
27
+ def notify(subscription, event, **options) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
28
+ case event
29
+ when :created
30
+ notify_subscription_created(subscription)
31
+ when :destroyed
32
+ notify_subscription_destroyed(subscription)
33
+ when :started
34
+ notify_subscription_started(subscription)
35
+ when :canceled
36
+ notify_subscription_canceled(subscription, options[:reason], options[:immediate])
37
+ when :due_for_renewal
38
+ notify_subscription_due_for_renewal(subscription)
39
+ when :renewed
40
+ notify_subscription_renewed(subscription)
41
+ when :renewal_payment_processing
42
+ notify_subscription_renewal_payment_processing(subscription)
43
+ when :grace_period_started
44
+ notify_subscription_grace_period_started(subscription, options[:days_remaining])
45
+ when :expired
46
+ notify_subscription_expired(subscription)
47
+ end
48
+ end
49
+
50
+ def notify_test_event
51
+ send_notification_to_listeners(nil, :on_test_event_received)
52
+ end
53
+
54
+ private
55
+
56
+ def notify_subscription_created(subscription)
57
+ send_notification_to_listeners(subscription, :on_subscription_created)
58
+ end
59
+
60
+ def notify_subscription_destroyed(subscription)
61
+ send_notification_to_listeners(subscription, :on_subscription_destroyed)
62
+ end
63
+
64
+ def notify_subscription_started(subscription)
65
+ send_notification_to_listeners(subscription, :on_subscription_started)
66
+ end
67
+
68
+ def notify_subscription_canceled(subscription, reason, immediate)
69
+ send_notification_to_listeners(subscription, :on_subscription_canceled, reason: reason, immediate: immediate)
70
+ end
71
+
72
+ def notify_subscription_due_for_renewal(subscription)
73
+ send_notification_to_listeners(subscription, :on_subscription_due_for_renewal)
74
+ end
75
+
76
+ def notify_subscription_renewed(subscription)
77
+ send_notification_to_listeners(subscription, :on_subscription_renewed)
78
+ end
79
+
80
+ def notify_subscription_renewal_payment_processing(subscription)
81
+ send_notification_to_listeners(subscription, :on_subscription_renewal_payment_processing)
82
+ end
83
+
84
+ def notify_subscription_expired(subscription)
85
+ send_notification_to_listeners(subscription, :on_subscription_expired)
86
+ end
87
+
88
+ def notify_subscription_grace_period_started(subscription, days_remaining)
89
+ send_notification_to_listeners(
90
+ subscription, :on_subscription_grace_period_started,
91
+ days_remaining: days_remaining
92
+ )
93
+ end
94
+
95
+ def notify_subscription_grace_period_exceeded(subscription)
96
+ send_notification_to_listeners(subscription, :on_subscription_grace_period_exceeded)
97
+ end
98
+
99
+ def send_notification_to_listeners(subscription, method_name, **args)
100
+ @listeners.each do |listener|
101
+ listener.send(method_name, subscription, **args) if listener.respond_to?(method_name)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CoreMerchant
4
- VERSION = "0.6.2"
4
+ VERSION = "0.8.0"
5
5
  end
data/lib/core_merchant.rb CHANGED
@@ -5,6 +5,8 @@ require "active_support/concern"
5
5
  require_relative "core_merchant/version"
6
6
  require_relative "core_merchant/customer_behavior"
7
7
  require_relative "core_merchant/subscription_plan"
8
+ require_relative "core_merchant/subscription_manager"
9
+ require_relative "core_merchant/subscription_listener"
8
10
 
9
11
  # CoreMerchant module
10
12
  module CoreMerchant
@@ -19,16 +21,25 @@ module CoreMerchant
19
21
 
20
22
  def configure
21
23
  yield(configuration)
24
+
25
+ return unless configuration.subscription_listener_class
26
+
27
+ listener = configuration.subscription_listener_class.constantize.new
28
+ subscription_manager.add_listener(listener)
22
29
  end
23
30
 
24
31
  def customer_class
25
32
  configuration.customer_class.constantize
26
33
  end
34
+
35
+ def subscription_manager
36
+ @subscription_manager ||= CoreMerchant::SubscriptionManager.new
37
+ end
27
38
  end
28
39
 
29
40
  # Used to configure CoreMerchant.
30
41
  class Configuration
31
- attr_accessor :customer_class
42
+ attr_accessor :customer_class, :subscription_listener_class
32
43
 
33
44
  def initialize
34
45
  @customer_class = "CoreMerchant::Customer"
@@ -36,15 +47,9 @@ module CoreMerchant
36
47
  end
37
48
 
38
49
  # Default customer class in CoreMerchant. Use this class if you don't have a model for customers in your application.
39
- class Customer
50
+ class Customer < ActiveRecord::Base
40
51
  include CustomerBehavior
41
52
 
42
53
  attr_accessor :id, :email, :name
43
-
44
- def initialize(id:, email:, name: nil)
45
- @id = id
46
- @email = email
47
- @name = name
48
- end
49
54
  end
50
55
  end
@@ -10,12 +10,8 @@ module CoreMerchant
10
10
 
11
11
  source_root File.expand_path("templates", __dir__)
12
12
 
13
- class_option :customer_class, type: :string, required: true,
14
- desc: "Name of your existing customer class, e.g. User"
15
-
16
13
  def copy_initializer
17
- @customer_class = options[:customer_class].classify
18
- template "core_merchant.erb", "config/initializers/core_merchant.rb"
14
+ template "core_merchant.rb", "config/initializers/core_merchant.rb"
19
15
  end
20
16
 
21
17
  def copy_locales
@@ -36,20 +32,22 @@ module CoreMerchant
36
32
 
37
33
  def show_post_install
38
34
  say "CoreMerchant has been successfully installed.", :green
39
- say <<~MESSAGE
40
- Customer class: #{@customer_class}. Please update this model to include the CoreMerchant::CustomerBehavior module.
35
+ next_steps = <<~MESSAGE
36
+ Next steps:
37
+ 1. Set the customer class in the initializer file (config/initializers/core_merchant.rb) to the class you want to use for customers.
38
+ 2. Create a subscription listener class (should include CoreMerchant::SubscriptionListener) in your app and set this class in the initializer file (config/initializers/core_merchant.rb) to the class you want to use for subscription listeners.
39
+ 3. Run `rails db:migrate` to create the subscription and subscription plan tables.
41
40
  MESSAGE
42
- say "Please run `rails db:migrate` to create the subscription and subscription plan tables.", :yellow
41
+ say next_steps, :yellow
43
42
  end
44
43
 
45
44
  def self.banner
46
- "rails generate core_merchant:install --customer_class=User"
45
+ "rails generate core_merchant:install"
47
46
  end
48
47
 
49
48
  def self.description
50
49
  <<~DESC
51
- Installs CoreMerchant into your application with the specified customer class.
52
- This could be User, Customer, or any other existing model in your application that represents a customer."
50
+ Installs CoreMerchant into your application. This generator will create an initializer file, migration files for the subscription and subscription plan tables, and a locale file."
53
51
  DESC
54
52
  end
55
53
  end
@@ -0,0 +1,26 @@
1
+ # Created by: rails generate core_merchant:install
2
+
3
+ CoreMerchant.configure do |config|
4
+ # Set the class that represents a customer in your application.
5
+ # This class must include the CoreMerchant::CustomerBehavior module as such:
6
+ # class User < ApplicationRecord
7
+ # include CoreMerchant::CustomerBehavior
8
+ # ...
9
+ # end
10
+ config.customer_class = "User"
11
+
12
+ # Set the class that will receive subscription notifications/
13
+ # This class must include the CoreMerchant::SubscriptionListener module.
14
+ # class SubscriptionListener
15
+ # include CoreMerchant::SubscriptionListener
16
+ # def on_subscription_created(subscription)
17
+ # ...
18
+ # end
19
+ # ...
20
+ # end
21
+ # This class will receive notifications when a subscription is created, updated, canceled etc.
22
+ #
23
+ # To test notifications to this class, you can override `on_test_event_received` method
24
+ # and call `CoreMerchant.subscription_manager.notify_test_event`.
25
+ config.subscription_listener_class = "SubscriptionListener"
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: core_merchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Seyithan Teymur
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-22 00:00:00.000000000 Z
11
+ date: 2024-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: factory_bot
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: generator_spec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -130,14 +144,18 @@ files:
130
144
  - Rakefile
131
145
  - core_merchant.gemspec
132
146
  - lib/core_merchant.rb
147
+ - lib/core_merchant/concerns/subscription_manager_renewals.rb
148
+ - lib/core_merchant/concerns/subscription_notifications.rb
133
149
  - lib/core_merchant/concerns/subscription_state_machine.rb
134
150
  - lib/core_merchant/customer_behavior.rb
135
151
  - lib/core_merchant/subscription.rb
152
+ - lib/core_merchant/subscription_listener.rb
153
+ - lib/core_merchant/subscription_manager.rb
136
154
  - lib/core_merchant/subscription_plan.rb
137
155
  - lib/core_merchant/version.rb
138
156
  - lib/generators/core_merchant/install_generator.rb
139
157
  - lib/generators/core_merchant/templates/core_merchant.en.yml
140
- - lib/generators/core_merchant/templates/core_merchant.erb
158
+ - lib/generators/core_merchant/templates/core_merchant.rb
141
159
  - lib/generators/core_merchant/templates/migrate/create_core_merchant_subscription_plans.erb
142
160
  - lib/generators/core_merchant/templates/migrate/create_core_merchant_subscriptions.erb
143
161
  - sig/core_merchant.rbs
@@ -1,5 +0,0 @@
1
- # Created by: rails generate core_merchant:install --customer_class=<%= @customer_class %>
2
-
3
- CoreMerchant.configure do |config|
4
- config.customer_class = "<%= @customer_class %>"
5
- end