mirror of
https://github.com/we-promise/sure
synced 2026-04-25 17:15:07 +02:00
fix: Show cancellation message when subscription is pending cancellation (#752)
* fix: Show cancellation message when subscription is pending cancellation When a subscription is cancelled via Stripe, the UI incorrectly showed "Your contribution continues on..." instead of reflecting the cancellation status. This fix adds tracking of `cancel_at_period_end` from Stripe webhooks and displays "Your contribution ends on..." when a subscription has been cancelled but is still active until the billing period ends. https://claude.ai/code/session_01Y8ELTdK1k9o315iSq43TRN * chore: Update schema.rb with cancel_at_period_end column https://claude.ai/code/session_01Y8ELTdK1k9o315iSq43TRN * Schema version --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,10 @@ module Family::Subscribeable
|
||||
subscription&.current_period_ends_at
|
||||
end
|
||||
|
||||
def subscription_pending_cancellation?
|
||||
subscription&.pending_cancellation?
|
||||
end
|
||||
|
||||
def start_subscription!(stripe_subscription_id)
|
||||
if subscription.present?
|
||||
subscription.update!(status: "active", stripe_id: stripe_subscription_id)
|
||||
|
||||
@@ -10,7 +10,8 @@ class Provider::Stripe::SubscriptionEventProcessor < Provider::Stripe::EventProc
|
||||
interval: subscription_details.plan.interval,
|
||||
amount: subscription_details.plan.amount / 100.0, # Stripe returns cents, we report dollars
|
||||
currency: subscription_details.plan.currency.upcase,
|
||||
current_period_ends_at: Time.at(subscription_details.current_period_end)
|
||||
current_period_ends_at: Time.at(subscription_details.current_period_end),
|
||||
cancel_at_period_end: subscription.cancel_at_period_end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -35,4 +35,8 @@ class Subscription < ApplicationRecord
|
||||
"Open demo"
|
||||
end
|
||||
end
|
||||
|
||||
def pending_cancellation?
|
||||
active? && cancel_at_period_end?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
<span>Currently on the <span class="font-medium"><%= @family.subscription.name %></span>.</span> <br />
|
||||
|
||||
<% if @family.next_payment_date %>
|
||||
<span><%= t("views.settings.payments.renewal", date: l(@family.next_payment_date, format: :long)) %></span>
|
||||
<% if @family.subscription_pending_cancellation? %>
|
||||
<span><%= t("views.settings.payments.cancellation", date: l(@family.next_payment_date, format: :long)) %></span>
|
||||
<% else %>
|
||||
<span><%= t("views.settings.payments.renewal", date: l(@family.next_payment_date, format: :long)) %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</p>
|
||||
<% elsif @family.trialing? %>
|
||||
|
||||
@@ -4,6 +4,7 @@ en:
|
||||
settings:
|
||||
payments:
|
||||
renewal: "Your contribution continues on %{date}."
|
||||
cancellation: "Your contribution ends on %{date}."
|
||||
settings:
|
||||
ai_prompts:
|
||||
show:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddCancelAtPeriodEndToSubscriptions < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
add_column :subscriptions, :cancel_at_period_end, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
3
db/schema.rb
generated
3
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_01_22_160000) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_01_23_000000) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pgcrypto"
|
||||
enable_extension "plpgsql"
|
||||
@@ -1259,6 +1259,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_01_22_160000) do
|
||||
t.datetime "trial_ends_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "cancel_at_period_end", default: false, null: false
|
||||
t.index ["family_id"], name: "index_subscriptions_on_family_id", unique: true
|
||||
end
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ class Provider::Stripe::SubscriptionEventProcessorTest < ActiveSupport::TestCase
|
||||
id: test_subscription_id,
|
||||
status: "active",
|
||||
customer: test_customer_id,
|
||||
cancel_at_period_end: false,
|
||||
items: {
|
||||
data: [
|
||||
{
|
||||
@@ -53,5 +54,52 @@ class Provider::Stripe::SubscriptionEventProcessorTest < ActiveSupport::TestCase
|
||||
assert_equal 9, family.subscription.amount
|
||||
assert_equal "USD", family.subscription.currency
|
||||
assert family.subscription.current_period_ends_at > 20.days.from_now
|
||||
assert_equal false, family.subscription.cancel_at_period_end
|
||||
end
|
||||
|
||||
test "handles subscription cancellation at period end" do
|
||||
test_customer_id = "test-customer-id-cancel"
|
||||
test_subscription_id = "test-subscription-id-cancel"
|
||||
|
||||
mock_event = JSON.parse({
|
||||
type: "customer.subscription.updated",
|
||||
data: {
|
||||
object: {
|
||||
id: test_subscription_id,
|
||||
status: "active",
|
||||
customer: test_customer_id,
|
||||
cancel_at_period_end: true,
|
||||
items: {
|
||||
data: [
|
||||
{
|
||||
current_period_end: 1.month.from_now.to_i,
|
||||
plan: {
|
||||
interval: "month",
|
||||
amount: 900,
|
||||
currency: "usd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}.to_json, object_class: OpenStruct)
|
||||
|
||||
family = Family.create!(
|
||||
name: "Test Cancelling Family",
|
||||
stripe_customer_id: test_customer_id
|
||||
)
|
||||
|
||||
family.start_subscription!(test_subscription_id)
|
||||
|
||||
processor = Provider::Stripe::SubscriptionEventProcessor.new(mock_event)
|
||||
processor.process
|
||||
|
||||
family.reload
|
||||
|
||||
assert_equal "active", family.subscription.status
|
||||
assert_equal true, family.subscription.cancel_at_period_end
|
||||
assert family.subscription.pending_cancellation?
|
||||
assert family.subscription_pending_cancellation?
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user