core_merchant 0.6.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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