Skip to content

Commit

Permalink
log chargebee webhook events to database
Browse files Browse the repository at this point in the history
  • Loading branch information
bhoggard committed Feb 9, 2025
1 parent cb18ab9 commit 0df4e60
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 24 deletions.
Binary file modified .yarn/install-state.gz
Binary file not shown.
59 changes: 39 additions & 20 deletions app/lib/webhook_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,35 @@
class WebhookHandler
class << self
def handle_payload(event)
cb_event = ChargebeeEvent.new(
event_id: event.id,
created_at: event.occurred_at,
event_type: event.event_type,
user_email: event.content.customer.email,
content: event.content
)
unless cb_event.save
Rails.logger.error("ChargebeeEvent failed: #{cb_event.errors.messages}")
return false
end

user = find_user(event)
if need_user(event) && !user
Rails.logger.error("User not found for #{event.content.customer.email}")
return true
end

case event.event_type
# when "payment_succeeded"
# upgrade_monthly_user(event)
# upgrade_monthly_user(user, event)
when "subscription_cancelled"
deactivate_user(event)
deactivate_user(user)
when "subscription_changed"
subscription_change(event)
subscription_change(user, event)
when "subscription_created"
subscription_create(event)
when "subscription_paused"
deactivate_user(event)
deactivate_user(user)
# when "subscription_reactivated"
# activate_user(event)
# when "subscription_resumed"
Expand All @@ -25,14 +43,17 @@ def handle_payload(event)

private

def activate_user(event)
def find_user(event)
customer = event.content.customer
user = User.find_by(cb_customer_id: customer.id)
unless user
Rails.logger.error("User not found for #{customer.id}")
return
end
User.find_by(cb_customer_id: customer.id)
end

# events with need users
def need_user(event)
event.event_type != "subscription_created"
end

def activate_user(user, event)
active = Util.subscription_is_annual_or_founding(event.content.subscription)

user.send_welcome_email if active && Rails.configuration.x.user_creation_send_email
Expand All @@ -41,23 +62,21 @@ def activate_user(event)
user.save
end

def deactivate_user(event)
customer = event.content.customer
user = User.find_by(cb_customer_id: customer.id)
return true unless user

def deactivate_user(user)
user.active = false
user.save
end

def subscription_change(event)
def subscription_change(user, event)
if Util.subscription_is_annual_or_founding(event.content.subscription)
activate_user(event)
user = find_user(event)
activate_user(user, event)
else
deactivate_user(event)
deactivate_user(user)
end
end

# TODO: handle creation for existing user
def subscription_create(event)
active = Util.subscription_is_annual_or_founding(event.content.subscription)

Expand All @@ -83,11 +102,11 @@ def subscription_create(event)
end
end

def upgrade_monthly_user(event)
def upgrade_monthly_user(user, event)
return true if Util.subscription_is_annual_or_founding(event.content.subscription)
started_at = event.content.subscription.started_at
if 2.years.ago.to_i > started_at
activate_user(event)
activate_user(user)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/chargebee_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ChargebeeEvent < ApplicationRecord
end
2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
resources :artworks do
member do
patch :move_artwork
end
end
resources :images do
member do
patch :move_image
Expand Down
13 changes: 13 additions & 0 deletions db/migrate/20250209185340_add_chargebee_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class AddChargebeeEvents < ActiveRecord::Migration[7.2]
def change
create_table :chargebee_events, id: false do |t|
t.string :event_id, null: false
t.datetime :created_at, null: false, index: true
t.string :event_type, null: false, index: true
t.string :user_email, null: false, index: true
t.jsonb :content
end
add_index :chargebee_events, :event_id, unique: true
add_index :chargebee_events, :content, using: :gin, name: :chargebee_events_idx
end
end
16 changes: 15 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 113 additions & 0 deletions test/fixtures/files/subscription_pause_scheduled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"id": "ev_AzqaRuUU8ubk53HJU",
"occurred_at": 1731622754,
"source": "admin_console",
"user": "[email protected]",
"object": "event",
"api_version": "v2",
"content": {
"subscription": {
"id": "2509882",
"billing_period": 1,
"billing_period_unit": "year",
"customer_id": "I-G3SKKUXLBL1B",
"status": "active",
"current_term_start": 1701208389,
"current_term_end": 1732830789,
"next_billing_at": 1740718800,
"created_at": 1669672389,
"started_at": 1669672389,
"activated_at": 1669672389,
"created_from_ip": "54.88.235.200",
"updated_at": 1731622754,
"has_scheduled_changes": false,
"channel": "web",
"resource_version": 1731622754615,
"deleted": false,
"object": "subscription",
"currency_code": "USD",
"subscription_items": [
{
"item_price_id": "Netvvrk-Annual-USD-Yearly",
"item_type": "plan",
"quantity": 1,
"unit_price": 79000,
"amount": 79000,
"free_quantity": 0,
"object": "subscription_item"
}
],
"shipping_address": {
"first_name": "Jane",
"last_name": "Bauman",
"email": "[email protected]",
"validation_status": "not_validated",
"object": "shipping_address"
},
"due_invoices_count": 0,
"mrr": 6583,
"exchange_rate": 1.0,
"base_currency_code": "USD",
"cf_Paypal_Migrated": "YES",
"pause_date": 1732830789,
"resume_date": 1740718800,
"has_scheduled_advance_invoices": false
},
"customer": {
"id": "I-G3SKKUXLBL1B",
"first_name": "Jane",
"last_name": "Bauman",
"email": "[email protected]",
"auto_collection": "off",
"net_term_days": 0,
"allow_direct_debit": false,
"created_at": 1699042118,
"created_from_ip": "27.5.153.91",
"taxability": "taxable",
"updated_at": 1712338306,
"pii_cleared": "active",
"channel": "web",
"resource_version": 1712338306433,
"deleted": false,
"object": "customer",
"billing_address": {
"country": "US",
"validation_status": "not_validated",
"object": "billing_address"
},
"card_status": "no_card",
"promotional_credits": 0,
"refundable_credits": 0,
"excess_payments": 0,
"unbilled_charges": 0,
"preferred_currency_code": "USD",
"mrr": 0,
"cf_Migrated": "YES",
"cf_Paypal": "YES"
}
},
"event_type": "subscription_pause_scheduled",
"webhook_status": "not_applicable",
"webhooks": [
{
"id": "whv2_16BdjQTta98S3V6B",
"webhook_status": "not_applicable",
"object": "webhook"
},
{
"id": "whv2_169lseTrjW9lv3Dj8",
"webhook_status": "succeeded",
"object": "webhook"
},
{
"id": "whv2_AzylISTta6fjqU6v",
"webhook_status": "not_applicable",
"object": "webhook"
},
{
"id": "whv2_AzyhBKU6RPt8W1gF7",
"webhook_status": "not_applicable",
"object": "webhook"
}
]
}
6 changes: 4 additions & 2 deletions test/lib/webhook_handler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ class WebhookHandlerTest < ActiveSupport::TestCase
payload = File.read(Rails.root.join("test", "fixtures", "files", "subscription_created_annual.json"))
event = ChargeBee::Event.deserialize(payload)

assert_difference "User.count" do
assert WebhookHandler.handle_payload(event)
assert_difference "ChargebeeEvent.count", 1 do
assert_difference "User.count" do
assert WebhookHandler.handle_payload(event)
end
end
end

Expand Down

0 comments on commit 0df4e60

Please sign in to comment.