Skip to content

Instantly share code, notes, and snippets.

@joemasilotti
Last active August 16, 2022 13:40
Show Gist options
  • Save joemasilotti/c0f7f3c4c31f67a81c13d10579af22be to your computer and use it in GitHub Desktop.
Save joemasilotti/c0f7f3c4c31f67a81c13d10579af22be to your computer and use it in GitHub Desktop.
Pay subscription changes

Goal: send a notification when a customer subscribes, churns, pauses, etc.

This is the barebones working version. Is there something I'm missing in Pay that could help with this?

If not, happy to clean it up and submit a PR if it will be valuable for others.

Note: This first-pass implementation is ugly, but the tests pass. There is obviously a lot to improve in terms of code quality, but my bigger focus is on the direction and idea.

module Pay
module SubscriptionExtensions
extend ActiveSupport::Concern
included do
after_commit :send_admin_notification
end
def send_admin_notification
case SubscriptionChanges.new(self)
when :subscribed
# subscribed
when :churned
# churned
when :resubscribed
# resubscribed
when :plan_changed
# plan_changes
when :paused
# paused
when :unpaused
# unpaused
end
end
end
end
Pay.setup do |config|
# ...
end
Rails.application.config.to_prepare do
Pay::Subscription.include Pay::SubscriptionExtensions
end
module Pay
class SubscriptionChanges
private attr_reader :subscription
def initialize(subscription)
@subscription = subscription
end
def status
ends_at_changes = subscription.previous_changes[:ends_at]
proccessor_plan_changes = subscription.previous_changes[:processor_plan]
data_changes = subscription.previous_changes[:data]
if ends_at_changes&.first.present? && ends_at_changes&.last.nil?
:resubscribed
elsif ends_at_changes&.first.nil? && ends_at_changes&.last.present?
:churned
elsif proccessor_plan_changes&.first.nil? && proccessor_plan_changes&.last.present?
:subscribed
elsif subscription.previous_changes.key?(:processor_plan)
:plan_changed
elsif data_changes.first[:pause_behavior].present? && data_changes.last[:pause_behavior].nil?
:unpaused
elsif data_changes.first[:pause_behavior].nil? && data_changes.last[:pause_behavior].present?
:paused
end
end
end
end
require "test_helper"
class Pay::SubscriptionChangesTest < ActiveSupport::TestCase
test "subscribed: processor_plan nil -> present" do
subscription = create_subscription!
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :subscribed, status
end
test "churned: ends_at nil -> present" do
subscription = create_subscription!
subscription.update!(ends_at: Date.tomorrow)
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :churned, status
end
test "resubscribed: ends_at present -> nil" do
subscription = create_subscription!(ends_at: Date.tomorrow)
subscription.update!(ends_at: nil)
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :resubscribed, status
end
test "plan_changed: processor_plan present -> present" do
subscription = create_subscription!
subscription.update!(processor_plan: :new_plan)
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :plan_changed, status
end
test "paused: pause_behavior nil -> present" do
subscription = create_subscription!
subscription.update!(data: {
pause_behavior: :void
})
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :paused, status
end
test "unpaused: pause_behavior present -> nil" do
subscription = create_subscription!(pause_behavior: :void)
subscription.update!(data: {
pause_behavior: nil
})
status = Pay::SubscriptionChanges.new(subscription).status
assert_equal :unpaused, status
end
def create_subscription!(ends_at: nil, pause_behavior: nil)
Pay::Subscription.create!(
customer: pay_customers(:one),
name: "sub_name",
processor_id: :sub_id,
processor_plan: :processor_plan,
status: :active,
ends_at: ends_at,
data: {
pause_behavior: pause_behavior
}
)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment