Skip to content

Commit 42ad855

Browse files
committed
šŸ› Fixed misleading "check your email" popup for already-subscribed members
closes https://linear.app/ghost/issue/BER-3724/show-correct-error-for-active-subscriber-checkout-attempts When a logged-in paid member clicked a paid signup button, e.g. a theme button using a `data-portal="signup/{tier}/{cadence}"` attribute, Portal showed the magic-link confirmation page even though the server rejects the checkout without sending any email, so members were told to check their inbox for an email that never arrived. The magic-link page is only correct for the anonymous flow, where the server emails a sign-in link before rejecting the checkout. Logged-in members now see an error notification explaining they already have an active subscription, reusing the existing translated string from the gift redemption flow.
1 parent 20c9e9e commit 42ad855

4 files changed

Lines changed: 54 additions & 2 deletions

File tree

ā€Žapps/portal/package.jsonā€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tryghost/portal",
3-
"version": "2.69.1",
3+
"version": "2.69.2",
44
"license": "MIT",
55
"repository": "https://github.com/TryGhost/Ghost",
66
"author": "Ghost Foundation",

ā€Žapps/portal/src/actions.jsā€Ž

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,16 @@ async function signup({data, state, api}) {
210210
};
211211
} catch (e) {
212212
if (e.code === CANNOT_CHECKOUT_WITH_EXISTING_SUBSCRIPTION) {
213+
if (state.member) {
214+
return {
215+
action: 'signup:failed',
216+
popupNotification: createPopupNotification({
217+
type: 'signup:failed', autoHide: false, closeable: true, state, status: 'error',
218+
message: t('You already have an active subscription.')
219+
})
220+
};
221+
}
222+
213223
return {
214224
page: 'magiclink',
215225
lastPage: 'signin',

ā€Žapps/portal/test/actions.test.tsā€Ž

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,48 @@ describe('signup action', () => {
8686
});
8787
expect(result).not.toHaveProperty('action', 'signup:failed');
8888
});
89+
90+
test('shows an error when a logged-in member checkout finds an existing subscription', async () => {
91+
const checkoutError = new Error('A subscription exists for this Member.') as Error & {code: string};
92+
checkoutError.code = 'CANNOT_CHECKOUT_WITH_EXISTING_SUBSCRIPTION';
93+
94+
const mockApi = {
95+
member: {
96+
checkoutPlan: vi.fn(() => Promise.reject(checkoutError))
97+
}
98+
};
99+
const state = {
100+
site: {},
101+
member: {
102+
name: 'Jamie Larson',
103+
email: 'jamie@example.com',
104+
paid: true
105+
}
106+
};
107+
108+
const result = await ActionHandler({
109+
action: 'signup',
110+
data: {
111+
plan: 'price_123',
112+
tierId: 'tier_123',
113+
cadence: 'month'
114+
},
115+
state,
116+
api: mockApi
117+
});
118+
119+
// No sign-in email is sent for authenticated members, so the
120+
// check-your-email page would be misleading.
121+
expect(result).not.toHaveProperty('page', 'magiclink');
122+
expect(result).toMatchObject({
123+
action: 'signup:failed',
124+
popupNotification: {
125+
type: 'signup:failed',
126+
status: 'error',
127+
message: 'You already have an active subscription.'
128+
}
129+
});
130+
});
89131
});
90132

91133
describe('redeemGift action', () => {

ā€Žghost/i18n/locales/context.jsonā€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@
399399
"X (Twitter)": "Share button in Portal share modal",
400400
"Yearly": "A label indicating an annual payment cadence",
401401
"Yesterday": "Time a comment was placed",
402-
"You already have an active subscription.": "Notification shown to a member who tries to redeem a gift subscription while they already have an active paid subscription",
402+
"You already have an active subscription.": "Notification shown to a member who tries to redeem a gift subscription or start a paid checkout while they already have an active paid subscription",
403403
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "Shown at the bottom of the newsletter. Status will be one of free/paid/trialing/complimentary - see those strings below.",
404404
"You can also copy & paste this URL into your browser:": "Descriptive text displayed underneath the buttons in login/signup emails, right before authentication URLs which can be copy/pasted",
405405
"You can unsubscribe from these notifications at {profileUrl}.": "Footer text in comments email to manage notification preferences for users",

0 commit comments

Comments
Ā (0)