diff --git a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap
index 3f7004a8cc..86adacd2b3 100644
--- a/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap
+++ b/ghost/core/test/integration/services/email-service/__snapshots__/batch-sending.test.js.snap
@@ -6809,14 +6809,13 @@ table.body figcaption a {
Subscription details
- You are receiving this because you are a free subscriber to Ghost.
-
+ You are receiving this because you are a free subscriber to Ghost.
- Name: Simon Tester
- Email: replacements-test-2@example.com
+ Name: not provided
+ Email: subscription-box-1@example.com
Member since: date
|
@@ -6958,7 +6957,27 @@ Subscription details
-You are receiving this because you are a free subscriber to Ghost.
+You are receiving this because you are a free subscriber to Ghost.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: subscription-box-1@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
@@ -6968,10 +6987,3805 @@ You are receiving this because you are a free subscriber to Ghost.
-Name: Simon Tester
-Email: replacements-test-2@example.com
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for canceled paid member 1 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a paid subscriber to Ghost. Your subscription has been canceled and will expire on date. You can resume your subscription via your account settings.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for canceled paid member 2 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-12/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-12/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a paid subscriber to Ghost. Your subscription has been canceled and will expire on date. You can resume your subscription via your account settings.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: canceled-paid@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for comped members 1 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a complimentary subscriber to Ghost.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for comped members 2 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-9/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-9/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a complimentary subscriber to Ghost.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: subscription-box-comped@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for free members 1 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a free subscriber to Ghost.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for free members 2 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-8/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-8/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a free subscriber to Ghost.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: subscription-box-1@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for paid member 1 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a paid subscriber to Ghost. Your subscription will renew on date.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for paid member 2 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-11/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-11/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a paid subscriber to Ghost. Your subscription will renew on date.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: paid@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for trialing member 1 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a trialing subscriber to Ghost. Your free trial ends on date, at which time you will be charged the regular price. You can always cancel before then.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for trialing member 2 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-10/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-10/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a trialing subscriber to Ghost. Your free trial ends on date, at which time you will be charged the regular price. You can always cancel before then.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: trialing-paid@example.com
+
+
+Member since: date
+
+
+
+
+Manage subscription → [http://127.0.0.1:2369/#/portal/account]
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost © 2023 – Unsubscribe [unsubscribe_url]
+
+
+
+https://ghost.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for trialing member 3 1`] = `
+Object {
+ "html": "
+
+
+
+
+
+ This is a test post title
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+ Subscription details
+
+ You are receiving this because you are a paid subscriber to Ghost. Your subscription will renew on date.
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+ |
+
+
+
+
+
+
+",
+}
+`;
+
+exports[`Batch sending tests Newsletter settings Shows subscription details box for trialing member 4 1`] = `
+Object {
+ "html": "
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Ghost [http://127.0.0.1:2369/]
+
+
+Daily newsletter [http://127.0.0.1:2369/]
+
+
+
+
+
+
+
+
+
+
+This is a test post title [http://127.0.0.1:2369/this-is-a-test-post-title-10/]
+
+
+
+
+
+
+
+
+
+By Joe Bloggs • 1 Jan 2023
+
+
+View in browser [http://127.0.0.1:2369/this-is-a-test-post-title-10/]
+
+
+
+
+
+
+
+
+
+
+
+Hello world
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Subscription details
+
+
+
+You are receiving this because you are a paid subscriber to Ghost. Your subscription will renew on date.
+
+
+
+
+
+
+
+
+Name: not provided
+
+
+Email: cus_c4f7911df8ef447a@example.com
Member since: date
diff --git a/ghost/core/test/integration/services/email-service/batch-sending.test.js b/ghost/core/test/integration/services/email-service/batch-sending.test.js
index d553df568b..10cf336bc7 100644
--- a/ghost/core/test/integration/services/email-service/batch-sending.test.js
+++ b/ghost/core/test/integration/services/email-service/batch-sending.test.js
@@ -13,7 +13,7 @@ const {settingsCache} = require('../../../../core/server/services/settings-helpe
const DomainEvents = require('@tryghost/domain-events');
const emailService = require('../../../../core/server/services/email-service');
const should = require('should');
-const {mockSetting} = require('../../../utils/e2e-framework-mock-manager');
+const {mockSetting, stripeMocker} = require('../../../utils/e2e-framework-mock-manager');
const mobileDocExample = '{"version":"0.3.1","atoms":[],"cards":[],"markups":[],"sections":[[1,"p",[[0,[],0,"Hello world"]]]],"ghostVersion":"4.0"}';
const mobileDocWithPaywall = '{"version":"0.3.1","markups":[],"atoms":[],"cards":[["paywall",{}]],"sections":[[1,"p",[[0,[],0,"Free content"]]],[10,0],[1,"p",[[0,[],0,"Members content"]]]]}';
@@ -263,6 +263,7 @@ describe('Batch sending tests', function () {
// Allows for setting stubbedSend during tests
return stubbedSend.call(this, ...arguments);
});
+ mockManager.mockStripe();
});
afterEach(async function () {
@@ -1039,19 +1040,189 @@ describe('Batch sending tests', function () {
await models.Newsletter.edit({show_comment_cta: true}, {id: defaultNewsletter.id});
});
- it('Shows subscription details box', async function () {
+ it('Shows subscription details box for free members', async function () {
+ // Create a new member without a first_name
+ await models.Member.add({
+ email: 'subscription-box-1@example.com',
+ labels: [{name: 'subscription-box-tests'}],
+ newsletters: [{
+ id: fixtureManager.get('newsletters', 0).id
+ }]
+ });
+
mockSetting('email_track_clicks', false); // Disable link replacement for this test
const defaultNewsletter = await getDefaultNewsletter();
await models.Newsletter.edit({show_subscription_details: true}, {id: defaultNewsletter.id});
- const {html} = await sendEmail({
+ const {html, plaintext} = await sendEmail({
title: 'This is a test post title',
mobiledoc: mobileDocExample
- });
+ }, 'label:subscription-box-tests');
// Currently the link is not present in plaintext version (because no text)
assert.equal(html.match(/#\/portal\/account/g).length, 1, 'Subscription details box should contain a link to the account page');
+
+ // Check text matches
+ assert.match(plaintext, /You are receiving this because you are a free subscriber to Ghost\./);
+
+ await lastEmailMatchSnapshot();
+
+ // undo
+ await models.Newsletter.edit({show_subscription_details: false}, {id: defaultNewsletter.id});
+ });
+
+ it('Shows subscription details box for comped members', async function () {
+ // Create a new member without a first_name
+ await models.Member.add({
+ email: 'subscription-box-comped@example.com',
+ labels: [{name: 'subscription-box-comped-tests'}],
+ newsletters: [{
+ id: fixtureManager.get('newsletters', 0).id
+ }],
+ status: 'comped'
+ });
+
+ mockSetting('email_track_clicks', false); // Disable link replacement for this test
+
+ const defaultNewsletter = await getDefaultNewsletter();
+ await models.Newsletter.edit({show_subscription_details: true}, {id: defaultNewsletter.id});
+
+ const {html, plaintext} = await sendEmail({
+ title: 'This is a test post title',
+ mobiledoc: mobileDocExample
+ }, 'label:subscription-box-comped-tests');
+
+ // Currently the link is not present in plaintext version (because no text)
+ assert.equal(html.match(/#\/portal\/account/g).length, 1, 'Subscription details box should contain a link to the account page');
+
+ // Check text matches
+ assert.match(plaintext, /You are receiving this because you are a complimentary subscriber to Ghost\./);
+
+ await lastEmailMatchSnapshot();
+
+ // undo
+ await models.Newsletter.edit({show_subscription_details: false}, {id: defaultNewsletter.id});
+ });
+
+ it('Shows subscription details box for trialing member', async function () {
+ mockSetting('email_track_clicks', false); // Disable link replacement for this test
+
+ // Create a new member without a first_name
+ const customer = stripeMocker.createCustomer({
+ email: 'trialing-paid@example.com'
+ });
+ const price = await stripeMocker.getPriceForTier('default-product', 'month');
+ await stripeMocker.createTrialSubscription({
+ customer,
+ price
+ });
+
+ const member = await models.Member.findOne({email: customer.email}, {require: true});
+ await models.Member.edit({
+ labels: [{name: 'subscription-box-trialing-tests'}],
+ newsletters: [{
+ id: fixtureManager.get('newsletters', 0).id
+ }]
+ }, {id: member.id});
+
+ const defaultNewsletter = await getDefaultNewsletter();
+ await models.Newsletter.edit({show_subscription_details: true}, {id: defaultNewsletter.id});
+
+ const {html, plaintext} = await sendEmail({
+ title: 'This is a test post title',
+ mobiledoc: mobileDocExample
+ }, 'label:subscription-box-trialing-tests');
+
+ // Currently the link is not present in plaintext version (because no text)
+ assert.equal(html.match(/#\/portal\/account/g).length, 1, 'Subscription details box should contain a link to the account page');
+
+ // Check text matches
+ assert.match(plaintext, /You are receiving this because you are a trialing subscriber to Ghost\. Your free trial ends on \d+ \w+ \d+, at which time you will be charged the regular price\. You can always cancel before then\./);
+
+ await lastEmailMatchSnapshot();
+
+ // undo
+ await models.Newsletter.edit({show_subscription_details: false}, {id: defaultNewsletter.id});
+ });
+
+ it('Shows subscription details box for paid member', async function () {
+ mockSetting('email_track_clicks', false); // Disable link replacement for this test
+
+ // Create a new member without a first_name
+ const customer = stripeMocker.createCustomer({
+ email: 'paid@example.com'
+ });
+ const price = await stripeMocker.getPriceForTier('default-product', 'month');
+ await stripeMocker.createSubscription({
+ customer,
+ price
+ });
+
+ const member = await models.Member.findOne({email: customer.email}, {require: true});
+ await models.Member.edit({
+ labels: [{name: 'subscription-box-paid-tests'}],
+ newsletters: [{
+ id: fixtureManager.get('newsletters', 0).id
+ }]
+ }, {id: member.id});
+
+ const defaultNewsletter = await getDefaultNewsletter();
+ await models.Newsletter.edit({show_subscription_details: true}, {id: defaultNewsletter.id});
+
+ const {html, plaintext} = await sendEmail({
+ title: 'This is a test post title',
+ mobiledoc: mobileDocExample
+ }, 'label:subscription-box-paid-tests');
+
+ // Currently the link is not present in plaintext version (because no text)
+ assert.equal(html.match(/#\/portal\/account/g).length, 1, 'Subscription details box should contain a link to the account page');
+
+ // Check text matches
+ assert.match(plaintext, /You are receiving this because you are a paid subscriber to Ghost\. Your subscription will renew on \d+ \w+ \d+\./);
+
+ await lastEmailMatchSnapshot();
+
+ // undo
+ await models.Newsletter.edit({show_subscription_details: false}, {id: defaultNewsletter.id});
+ });
+
+ it('Shows subscription details box for canceled paid member', async function () {
+ mockSetting('email_track_clicks', false); // Disable link replacement for this test
+
+ // Create a new member without a first_name
+ const customer = stripeMocker.createCustomer({
+ email: 'canceled-paid@example.com'
+ });
+ const price = await stripeMocker.getPriceForTier('default-product', 'month');
+ await stripeMocker.createSubscription({
+ customer,
+ price,
+ cancel_at_period_end: true
+ });
+
+ const member = await models.Member.findOne({email: customer.email}, {require: true});
+ await models.Member.edit({
+ labels: [{name: 'subscription-box-canceled-tests'}],
+ newsletters: [{
+ id: fixtureManager.get('newsletters', 0).id
+ }]
+ }, {id: member.id});
+
+ const defaultNewsletter = await getDefaultNewsletter();
+ await models.Newsletter.edit({show_subscription_details: true}, {id: defaultNewsletter.id});
+
+ const {html, plaintext} = await sendEmail({
+ title: 'This is a test post title',
+ mobiledoc: mobileDocExample
+ }, 'label:subscription-box-canceled-tests');
+
+ // Currently the link is not present in plaintext version (because no text)
+ assert.equal(html.match(/#\/portal\/account/g).length, 1, 'Subscription details box should contain a link to the account page');
+
+ // Check text matches
+ assert.match(plaintext, /You are receiving this because you are a paid subscriber to Ghost\. Your subscription has been canceled and will expire on \d+ \w+ \d+\. You can resume your subscription via your account settings\./);
+
await lastEmailMatchSnapshot();
// undo
diff --git a/ghost/core/test/utils/stripe-mocker.js b/ghost/core/test/utils/stripe-mocker.js
index 083a7d5dc7..a7d2e4fb8b 100644
--- a/ghost/core/test/utils/stripe-mocker.js
+++ b/ghost/core/test/utils/stripe-mocker.js
@@ -85,7 +85,8 @@ class StripeMocker {
customer,
price,
status: 'trialing',
- trial_end_at: (Date.now() + 1000 * 60 * 60 * 24 * 7) / 1000,
+ trial_start: Date.now() / 1000,
+ trial_end: (Date.now() + 1000 * 60 * 60 * 24 * 7) / 1000,
...overrides
});
}
diff --git a/ghost/email-service/lib/email-renderer.js b/ghost/email-service/lib/email-renderer.js
index af6239309e..c58522344a 100644
--- a/ghost/email-service/lib/email-renderer.js
+++ b/ghost/email-service/lib/email-renderer.js
@@ -11,7 +11,7 @@ const tpl = require('@tryghost/tpl');
const messages = {
subscriptionStatus: {
- free: 'You are currently subscribed to the free plan.',
+ free: '',
expired: 'Your subscription has expired.',
canceled: 'Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.',
active: 'Your subscription will renew on {date}.',
@@ -405,6 +405,29 @@ class EmailRenderer {
return url.href;
}
+ /**
+ * Returns whether a paid member is trialing a subscription
+ */
+ isMemberTrialing(member) {
+ // Do we have an active subscription?
+ if (member.status === 'paid') {
+ let activeSubscription = member.subscriptions.find((subscription) => {
+ return subscription.status === 'trialing';
+ });
+
+ if (!activeSubscription) {
+ return false;
+ }
+
+ // Translate to a human readable string
+ if (activeSubscription.trial_end_at && activeSubscription.trial_end_at > new Date() && activeSubscription.status === 'trialing') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* @param {MemberLike} member
* @returns {string}
@@ -528,6 +551,9 @@ class EmailRenderer {
if (member.status === 'comped') {
return 'complimentary';
}
+ if (this.isMemberTrialing(member)) {
+ return 'trialing';
+ }
return member.status;
}
},
diff --git a/ghost/email-service/lib/email-templates/template.hbs b/ghost/email-service/lib/email-templates/template.hbs
index b686785fb2..aeacc39c0e 100644
--- a/ghost/email-service/lib/email-templates/template.hbs
+++ b/ghost/email-service/lib/email-templates/template.hbs
@@ -180,8 +180,7 @@
|
Subscription details
- You are receiving this because you are a %%{status}%% subscriber to {{site.title}}.
- %%{status_text}%%
+ You are receiving this because you are a %%{status}%% subscriber to {{site.title}}. %%{status_text}%%
diff --git a/ghost/email-service/test/email-renderer.test.js b/ghost/email-service/test/email-renderer.test.js
index 1ee4c34a41..2b5403120e 100644
--- a/ghost/email-service/test/email-renderer.test.js
+++ b/ghost/email-service/test/email-renderer.test.js
@@ -203,6 +203,24 @@ describe('Email renderer', function () {
assert.equal(replacements[0].getValue(member), 'complimentary');
});
+ it('returns mapped trialing status', function () {
+ member.status = 'paid';
+ member.subscriptions = [
+ {
+ status: 'trialing',
+ trial_end_at: new Date(2050, 2, 13, 12, 0),
+ current_period_end: new Date(2023, 2, 13, 12, 0),
+ cancel_at_period_end: false
+ }
+ ];
+ const html = 'Hello %%{status}%%,';
+ const replacements = emailRenderer.buildReplacementDefinitions({html, newsletterUuid: newsletter.get('uuid')});
+ assert.equal(replacements.length, 1);
+ assert.equal(replacements[0].token.toString(), '/%%\\{status\\}%%/g');
+ assert.equal(replacements[0].id, 'status');
+ assert.equal(replacements[0].getValue(member), 'trialing');
+ });
+
it('returns manage_account_url', function () {
const html = 'Hello %%{manage_account_url}%%,';
const replacements = emailRenderer.buildReplacementDefinitions({html, newsletterUuid: newsletter.get('uuid')});
@@ -214,11 +232,21 @@ describe('Email renderer', function () {
it('returns status_text', function () {
const html = 'Hello %%{status_text}%%,';
+ member.status = 'paid';
+ member.subscriptions = [
+ {
+ status: 'trialing',
+ trial_end_at: new Date(2050, 2, 13, 12, 0),
+ current_period_end: new Date(2023, 2, 13, 12, 0),
+ cancel_at_period_end: false
+ }
+ ];
+
const replacements = emailRenderer.buildReplacementDefinitions({html, newsletterUuid: newsletter.get('uuid')});
assert.equal(replacements.length, 1);
assert.equal(replacements[0].token.toString(), '/%%\\{status_text\\}%%/g');
assert.equal(replacements[0].id, 'status_text');
- assert.equal(replacements[0].getValue(member), 'You are currently subscribed to the free plan.');
+ assert.equal(replacements[0].getValue(member), 'Your free trial ends on 13 March 2050, at which time you will be charged the regular price. You can always cancel before then.');
});
it('returns correct createdAt', function () {
@@ -279,6 +307,109 @@ describe('Email renderer', function () {
});
});
+ describe('isMemberTrialing', function () {
+ let emailRenderer;
+
+ beforeEach(function () {
+ emailRenderer = new EmailRenderer({
+ urlUtils: {
+ urlFor: () => 'http://example.com/subdirectory/'
+ },
+ labs: {
+ isSet: () => true
+ },
+ settingsCache: {
+ get: (key) => {
+ if (key === 'timezone') {
+ return 'UTC';
+ }
+ }
+ }
+ });
+ });
+
+ it('Returns false for free member', function () {
+ const member = {
+ id: '456',
+ uuid: 'myuuid',
+ name: 'Test User',
+ email: 'test@example.com',
+ createdAt: new Date(2023, 2, 13, 12, 0),
+ status: 'free'
+ };
+
+ const result = emailRenderer.isMemberTrialing(member);
+ assert.equal(result, false);
+ });
+
+ it('Returns false for paid member without trial', function () {
+ const member = {
+ id: '456',
+ uuid: 'myuuid',
+ name: 'Test User',
+ email: 'test@example.com',
+ createdAt: new Date(2023, 2, 13, 12, 0),
+ status: 'paid',
+ subscriptions: [
+ {
+ status: 'active',
+ current_period_end: new Date(2023, 2, 13, 12, 0),
+ cancel_at_period_end: false
+ }
+ ]
+ };
+
+ const result = emailRenderer.isMemberTrialing(member);
+ assert.equal(result, false);
+ });
+
+ it('Returns true for trialing paid member', function () {
+ const member = {
+ id: '456',
+ uuid: 'myuuid',
+ name: 'Test User',
+ email: 'test@example.com',
+ createdAt: new Date(2023, 2, 13, 12, 0),
+ status: 'paid',
+ subscriptions: [
+ {
+ status: 'trialing',
+ trial_end_at: new Date(2050, 2, 13, 12, 0),
+ current_period_end: new Date(2023, 2, 13, 12, 0),
+ cancel_at_period_end: false
+ }
+ ],
+ tiers: []
+ };
+
+ const result = emailRenderer.isMemberTrialing(member);
+ assert.equal(result, true);
+ });
+
+ it('Returns false for expired trialing paid member', function () {
+ const member = {
+ id: '456',
+ uuid: 'myuuid',
+ name: 'Test User',
+ email: 'test@example.com',
+ createdAt: new Date(2023, 2, 13, 12, 0),
+ status: 'paid',
+ subscriptions: [
+ {
+ status: 'trialing',
+ trial_end_at: new Date(2000, 2, 13, 12, 0),
+ current_period_end: new Date(2023, 2, 13, 12, 0),
+ cancel_at_period_end: false
+ }
+ ],
+ tiers: []
+ };
+
+ const result = emailRenderer.isMemberTrialing(member);
+ assert.equal(result, false);
+ });
+ });
+
describe('getMemberStatusText', function () {
let emailRenderer;
@@ -311,7 +442,7 @@ describe('Email renderer', function () {
};
const result = emailRenderer.getMemberStatusText(member);
- assert.equal(result, 'You are currently subscribed to the free plan.');
+ assert.equal(result, '');
});
it('Returns for active paid member', function () {
| |