From 6f9f2c32c9c536817e6c52bf631e93d0c1506872 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Wed, 29 Oct 2025 15:49:41 +0530 Subject: [PATCH] handle cancellation properly --- .../v2/subscription_provisioning_service.rb | 59 +++++++++++++++++-- .../billing/v2/webhook_handler_service.rb | 9 +++ 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/enterprise/app/services/enterprise/billing/v2/subscription_provisioning_service.rb b/enterprise/app/services/enterprise/billing/v2/subscription_provisioning_service.rb index 64d684686..8c42fb8ef 100644 --- a/enterprise/app/services/enterprise/billing/v2/subscription_provisioning_service.rb +++ b/enterprise/app/services/enterprise/billing/v2/subscription_provisioning_service.rb @@ -7,8 +7,9 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil # Extract details from the subscription pricing_plan_id = extract_pricing_plan_id(subscription) quantity = extract_subscription_quantity(subscription) + billing_cadence = extract_billing_cadence(subscription) # Update account with subscription details - update_subscription_details(subscription_id, pricing_plan_id, quantity) + update_subscription_details(subscription_id, pricing_plan_id, quantity, billing_cadence) # Provision the subscription: sync credits and enable features provision_subscription(pricing_plan_id) if pricing_plan_id.present? @@ -18,6 +19,22 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil { success: false, message: "Stripe error: #{e.message}" } end + def refresh + subscription_id = account.custom_attributes['stripe_subscription_id'] + return if subscription_id.blank? + + subscription_plan = retrieve_pricing_plan_subscription(subscription_id) + servicing_status = servicing_status(subscription_plan) + pricing_plan_id = extract_pricing_plan_id(subscription_plan) + if servicing_status == 'canceled' + cancel_subscription + else + quantity = account.custom_attributes['subscribed_quantity'] + billing_cadence = extract_billing_cadence(subscription_plan) + update_subscription_details(subscription_id, pricing_plan_id, quantity, billing_cadence) + end + end + private def build_success_response(subscription_id, pricing_plan_id, quantity) @@ -30,6 +47,36 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil } end + def servicing_status(subscription_plan) + subscription_plan.respond_to?(:servicing_status) ? subscription_plan.servicing_status : subscription_plan['servicing_status'] + end + + def cancel_subscription + hacker_plan_config = InstallationConfig.find_by(name: 'STRIPE_HACKER_PLAN_ID') + pricing_plan_id = hacker_plan_config.value + + # Update subscription status and plan details + attributes = { + 'plan_name': 'Hacker', + 'stripe_pricing_plan_id': pricing_plan_id, + 'subscribed_quantity': 2, + 'stripe_subscription_id': nil, + 'billing_cadence': nil, + 'subscription_status': 'canceled' + } + update_custom_attributes(attributes) + + # Sync credits for Hacker plan (0 credits) + sync_plan_credits(pricing_plan_id) + + # Disable all premium features and save + disable_all_premium_features + account.save! + + # Reset captain usage + reset_captain_usage + end + def retrieve_pricing_plan_subscription(subscription_id) StripeV2Client.request( :get, @@ -44,6 +91,10 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil subscription.respond_to?(:pricing_plan) ? subscription.pricing_plan : subscription['pricing_plan'] end + def extract_billing_cadence(subscription) + subscription.respond_to?(:billing_cadence) ? subscription.billing_cadence : subscription['billing_cadence'] + end + def extract_subscription_quantity(_subscription) # Get quantity from account custom_attributes (set during checkout) pending_quantity = account.custom_attributes['pending_subscription_quantity'] @@ -59,7 +110,7 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil 1 end - def update_subscription_details(subscription_id, pricing_plan_id, quantity) + def update_subscription_details(subscription_id, pricing_plan_id, quantity, billing_cadence) Rails.logger.info "[V2 Billing] Updating subscription details: subscription_id=#{subscription_id}, " \ "pricing_plan_id=#{pricing_plan_id}, quantity=#{quantity}" @@ -69,7 +120,8 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil 'subscribed_quantity' => quantity, 'subscription_status' => 'active', 'pending_subscription_quantity' => nil, - 'pending_subscription_pricing_plan' => nil + 'pending_subscription_pricing_plan' => nil, + 'billing_cadence' => billing_cadence } attributes['stripe_pricing_plan_id'] = pricing_plan_id if pricing_plan_id.present? @@ -99,7 +151,6 @@ class Enterprise::Billing::V2::SubscriptionProvisioningService < Enterprise::Bil def sync_plan_credits(pricing_plan_id) plan_credits = Enterprise::Billing::V2::PlanCatalog.monthly_credits_for(pricing_plan_id) - return unless plan_credits Enterprise::Billing::V2::CreditManagementService .new(account: account) diff --git a/enterprise/app/services/enterprise/billing/v2/webhook_handler_service.rb b/enterprise/app/services/enterprise/billing/v2/webhook_handler_service.rb index e5a7618b5..6446adbcb 100644 --- a/enterprise/app/services/enterprise/billing/v2/webhook_handler_service.rb +++ b/enterprise/app/services/enterprise/billing/v2/webhook_handler_service.rb @@ -8,6 +8,9 @@ class Enterprise::Billing::V2::WebhookHandlerService when 'v2.billing.pricing_plan_subscription.servicing_activated' Rails.logger.info "Handling subscription servicing activated event: #{@event.related_object.id}" handle_subscription_servicing_activated(@event.related_object.id) + when 'v2.billing.cadence.billed' + Rails.logger.info "Handling cadence billed event: #{@event.related_object.id}" + refresh_account_subscription_details(@event.related_object.id) else { success: true } end @@ -56,6 +59,12 @@ class Enterprise::Billing::V2::WebhookHandlerService .provision(subscription_id: subscription_id) end + def refresh_account_subscription_details(_cadence_id) + Enterprise::Billing::V2::SubscriptionProvisioningService + .new(account: account) + .refresh + end + def stripe_api_options { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } end