Skip to content

Commit 9d6eb28

Browse files
committed
🐛 Fixed cancelled subscriptions counting in multiple subscriptions warning
ref https://linear.app/ghost/issue/BER-3717/ - Stripe keeps a cancelled subscription's status as "active" until the end of its billing period, so members stayed in the multiple-active-subscriptions filter and warning banner even after their extra subscriptions had been cancelled - the filter now also requires cancel_at_period_end to be false, so cancelling an extra subscription immediately removes the member from the filter and banner count
1 parent b9b2f65 commit 9d6eb28

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

ghost/core/core/server/models/member.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ const Member = ghostBookshelf.Model.extend({
210210
.from('members_stripe_customers')
211211
.innerJoin('members_stripe_customers_subscriptions', 'members_stripe_customers_subscriptions.customer_id', 'members_stripe_customers.customer_id')
212212
.where('members_stripe_customers_subscriptions.status', 'active')
213+
.where('members_stripe_customers_subscriptions.cancel_at_period_end', false)
213214
.groupBy('members_stripe_customers.member_id')
214215
.havingRaw('COUNT(DISTINCT members_stripe_customers_subscriptions.customer_id) > 1')
215216
.as('multiple_active_stripe_customers');

ghost/core/test/e2e-api/admin/members.test.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async function createGiftMember(data) {
7878
return member;
7979
}
8080

81-
async function createStripeCustomerWithSubscription(member, customerId, subscriptionId, status = 'active') {
81+
async function createStripeCustomerWithSubscription(member, customerId, subscriptionId, {status = 'active', cancelAtPeriodEnd = false} = {}) {
8282
const now = new Date();
8383

8484
await knex('members_stripe_customers').insert({
@@ -95,6 +95,7 @@ async function createStripeCustomerWithSubscription(member, customerId, subscrip
9595
customer_id: customerId,
9696
subscription_id: subscriptionId,
9797
status,
98+
cancel_at_period_end: cancelAtPeriodEnd,
9899
current_period_end: now,
99100
start_date: now,
100101
created_at: now,
@@ -690,9 +691,10 @@ describe('Members API', function () {
690691
const emails = [
691692
'multiple-active-stripe-customers@example.com',
692693
'same-active-stripe-customer@example.com',
693-
'trialing-stripe-customers@example.com'
694+
'trialing-stripe-customers@example.com',
695+
'cancelling-stripe-customers@example.com'
694696
];
695-
const customerIds = ['cus_matching_1', 'cus_matching_2', 'cus_same_1', 'cus_trialing_1', 'cus_trialing_2'];
697+
const customerIds = ['cus_matching_1', 'cus_matching_2', 'cus_same_1', 'cus_trialing_1', 'cus_trialing_2', 'cus_cancelling_1', 'cus_cancelling_2'];
696698

697699
try {
698700
const matchingMember = await createMember({
@@ -727,8 +729,15 @@ describe('Members API', function () {
727729
email: emails[2],
728730
status: 'free'
729731
});
730-
await createStripeCustomerWithSubscription(trialingMember, 'cus_trialing_1', 'sub_trialing_1', 'trialing');
731-
await createStripeCustomerWithSubscription(trialingMember, 'cus_trialing_2', 'sub_trialing_2', 'trialing');
732+
await createStripeCustomerWithSubscription(trialingMember, 'cus_trialing_1', 'sub_trialing_1', {status: 'trialing'});
733+
await createStripeCustomerWithSubscription(trialingMember, 'cus_trialing_2', 'sub_trialing_2', {status: 'trialing'});
734+
735+
const cancellingMember = await createMember({
736+
email: emails[3],
737+
status: 'free'
738+
});
739+
await createStripeCustomerWithSubscription(cancellingMember, 'cus_cancelling_1', 'sub_cancelling_1');
740+
await createStripeCustomerWithSubscription(cancellingMember, 'cus_cancelling_2', 'sub_cancelling_2', {cancelAtPeriodEnd: true});
732741

733742
const filter = encodeURIComponent('count.active_stripe_customers:>1');
734743
const res = await agent

0 commit comments

Comments
 (0)