Commit Graph

1035 Commits

Author SHA1 Message Date
Sag
e476eebd2d
🎨 Added staff notification when a sub is canceled due to failed payments (#20534)
ref https://linear.app/tryghost/issue/ENG-1254

- when a subscription is canceled automatically by Stripe (e.g. due to
multiple failed payments), we now send a staff notification
- logic before: if a member cancels a sub in Portal, then send a staff
notification
- logic now: if a subscription was active, but is now set to cancel
immediately or at the end of the billing period, then send a staff
notification.
- with that logic change, we now send a cancellation staff notification
when:
    1. A member cancels their sub in Portal (existing)
    2. A staff member cancels a member sub in Stripe (new)
    3. A staff member cancels a member sub in Admin (new)
    4. A sub is canceled automatically by Stripe because of multiple failed
payments (new)
- the copy of the staff notification email has also been updated to take
into account 1) manual vs automatic cancellations, and 2) immediate vs
end of billing period cancellations
2024-07-15 08:07:18 +02:00
Chris Raible
bf895e6e99
🐛 Fixed offer redemptions for free members redeeming an offer (#20571)
ref
https://linear.app/tryghost/issue/ENG-1251/support-escalation-re-offers-not-tracking

- Offer Redemptions were not being persisted in the database for
existing free members who upgrade to a paid plan with an offer, which
resulted in inaccurate offer redemption counts. This made it difficult
to assess the performance of an offer.
- Previously, Ghost recorded an offer redemption in the DB in response
to the `SubscriptionCreatedEvent`, under the assumption that the offer
details would be included in this event. This assumption was valid for
brand new members starting a subscription with an offer, but not for
existing free members upgrading to a paid plan with an offer.
- For existing free members, the subscription is first stored in Ghost
in response to the `customer.subscription.created` Stripe webhook. At
this point, the offer/discount is not attached to the subscription, so
the `SubscriptionCreatedEvent` triggers without the offer information,
and the offer redemption is not recorded. After the
`checkout.session.completed` webhook is received (which _does_ include
the offer details), the subscription is updated in Ghost, but the Offer
Redemption is not stored.
- For brand new members, the `customer.subscription.created` webhook
no-ops, because the member and Stripe Customer don't exist yet.
Therefore, the subscription is first created in Ghost in response to the
`checkout.session.completed` webhook, which _does_ include the offer
information, so the offer information is included in the
`SubscriptionCreatedEvent` and the offer redemption is recorded as
expected.
- This change adds a new `OfferRedemptionEvent`, which triggers
either: (1) when a new subscription is created with an offer (as in the
case of a brand new member), or (2) when an existing subscription is
first updated to include an offer (as in the case of an existing free
member upgrading with an offer). The Offer Redemption is then persisted
in the DB in response to the `OfferRedemptionEvent` rather than the
`SubscriptionCreatedEvent`.
2024-07-09 16:05:26 -07:00
Sag
6e0b009034
🎨 Added 'Payment failed' subscription cancellation reason (#20527)
ref https://linear.app/tryghost/issue/ENG-1254

- we currently only store a cancellation reason when a member cancels
manually in Portal
- we now also store "Payment failed" when the cancellation is automatic
due to several payment failures
2024-07-03 13:12:01 +02:00
Sag
7f963e9c2a
🎨 Added 'Changed email address' event to Member Activity (#20493)
fixes https://linear.app/tryghost/issue/ENG-1256

- when a member changes their email address, surface it in Member
Activity
2024-07-01 15:33:33 +00:00
Princi Vershwal
7bffe5b79a
Added option param to skip distinct from count query for members API
ref https://linear.app/tryghost/issue/SLO-173/removed-distinct-from-member-count-query

Performance of GET /members API can be improved by dropping the distinct from the total members count query.

select count(distinct members.id) as aggregate from `members`; // 275ms
select count(*) as aggregate from `members`; // 30ms

In this case we know that the result set will always be unique.
2024-06-27 17:35:19 +05:30
Sag
725ebc3e9f
Fixed invalid tierId handling during member paid checkout (#20455)
- fixes https://linear.app/tryghost/issue/SLO-90
2024-06-24 15:33:39 +00:00
Michael Barrett
7f92777f89
Added logging to track offer redemption (#20329)
refs
[ONC-56](https://linear.app/tryghost/issue/ONC-56/support-escalation-re-offers-not-tracking)

Added logging to track offer redemption logic to debug issue with offer
redemptions tracking incorrectly
2024-06-05 17:48:43 +01:00
Sag
d751d648c7
Fixed offer not found case during Stripe checkout (#20322)
fixes https://linear.app/tryghost/issue/SLO-135

- handles edge cases when an invalid `offerId` is provided during Stripe
checkout
2024-06-04 10:27:45 +00:00
renovate[bot]
3ebe206ea7 Update TryGhost packages 2024-05-27 16:58:32 +02:00
Steve Larson
842290cbef
Improved performance for filter strings with multiple neq statements (#20198)
ref https://linear.app/tryghost/issue/CFR-27
- updated packages to include performance improvement for NQL filter
strings including multiple neq filters for the same resource
- bumped `bookshelf-plugins`
- bumped NQL versions

We identified a performance fix that allows us to combine not equal
(neq) filters for the same resource in a logically-equivalent way that
also has far more performant resulting SQL.

We're effectively automatically combining strings like
'tag:-tag1+tag:-tag2` into 'tag:-[tag1,tag2]'.
2024-05-13 10:35:27 -05:00
Sag
cb8213e7d3
Fixed validation when tierId is missing during Stripe checkout (#20195)
refs https://linear.app/tryghost/issue/SLO-90
refs
https://www.notion.so/ghost/Decoupling-Members-from-Stripe-13b644d4dccb43ea83f683473c690b82

- the members API didn't support passing a Stripe Price ID directly
during checkout since end of 2022. However, we did not update the param
validation accordingly
2024-05-13 14:47:39 +02:00
Fabien 'egg' O'Carroll
56d984f05f
Used subscription currency for setup session (#19991)
ref https://linear.app/tryghost/issue/ENG-812
ref https://github.com/TryGhost/Ghost/commit/5b694761bc

We wanna use the currency of the subscription to avoid the edge-case where the 
subscription currency doesn't match the sites current tiers currency.
2024-05-09 13:03:11 +00:00
Sag
60ac3c735b
🐛 Fixed updating payment method when beta flag is on (#20171)
refs https://linear.app/tryghost/issue/ONC-20
refs https://linear.app/tryghost/issue/ENG-867

- when using dynamic payment methods in Stripe, we need to provide a
currency. Stripe uses that parameter to determine which payment methods
to render
- docs: https://docs.stripe.com/api/checkout/sessions/create
2024-05-08 20:56:17 +02:00
Daniel Lockyer
265a8dd16f Added function names to more middleware
refs 319f251ad2

- this helps debugging because all middleware in the stack will have a
  function name, so it'll show up instead of `<anonymous>`
2024-05-06 17:51:39 +02:00
Chris Raible
b9f7ea65e9
Revert "Added new member signup flow behind labs flag (#19986)" (#20130)
ref https://linear.app/tryghost/issue/KTLO-1/members-spam-signups

This reverts commit 01d0b2b304.

- Removed the new member signup flow because it didn't solve the
problems with spam signups
2024-05-02 13:02:32 -07:00
renovate[bot]
a33dccf8cd Update TryGhost packages 2024-05-01 17:01:41 +02:00
Daniel Lockyer
3f7a7fff44 Fixed HTTP 500 when adding unknown products to member
fix https://linear.app/tryghost/issue/SLO-89/cannot-read-properties-of-null-reading-get-an-unexpected-error

- if we pass an invalid ID when updating the products on a member, we
  throw a HTTP 500 error because `product` is `null`
- we can check for this and return a BadRequestError, because the user
  supplied an incorrect ID
2024-05-01 16:54:35 +02:00
Daniel Lockyer
31bdef94cd Handled invalid filters in members event repository
fix https://linear.app/tryghost/issue/SLO-82/query-error-unexpected-character-in-filter-at-char-1

- previously, we weren't handling a parsing error, and just bubbling it
  back up the chain
- this would result in an InternalServerError somewhere, which caused
  500s
- we can handle this, because it's just a bad filter
- this adds handling so we return a 422 upon receiving an invalid filter
2024-05-01 11:58:09 +02:00
renovate[bot]
60a3b5a913 Update TryGhost packages 2024-05-01 08:49:04 +02:00
renovate[bot]
8f839b34b1 Update Types packages 2024-04-30 22:00:55 +02:00
Chris Raible
01d0b2b304
Added new member signup flow behind labs flag (#19986)
ref https://linear.app/tryghost/issue/KTLO-1/members-spam-signups

- Some customers are seeing many spammy signups ("hundreds a day") — our
hypothesis is that bots and/or email link checkers are able to signup by
simply following the link in the email without even loading the page in
a browser.
- Currently new members signup by clicking a magic link in an email,
which is a simple GET request. When the user (or a bot) clicks that link, Ghost
creates the member and signs them in for the first time.
- This change, behind an alpha flag, requires a new member to click the
link in the email, which takes them to a new frontend route `/confirm_signup/`, then submit a form on the page which sends a POST request to the
server. If JavaScript is enabled, the form will be submitted
automatically so the only change to the user is an extra flash/redirect
before being signed in and redirected to the homepage.
- This change is behind the alpha flag `membersSpamPrevention` so we can
test it out on a few customer's sites and see if it helps reduce the
spam signups. With the flag off, the signup flow remains the same as
before.
2024-04-04 15:25:41 -07:00
renovate[bot]
dcbbfbba70 Update dependency express to v4.19.2 [SECURITY] 2024-03-27 11:18:44 +01:00
Simon Backx
3b8fb3cedf
Added support for ignoring migrated (duplicate) subscriptions (#19902)
refs KTLO-19

When we need to migrate subscriptions from a platform with platform
fees, we need to recreate the subscriptions. That can cause the same
subscription to be attached multiple times to the same member in Ghost.

This is a problem because all MRR, subscriptions and cancellations stats
are no longer correct. Ghost will add a MRR event for the duplicated
subscription from the start time, so there is a sudden peak in MRR and a
dip after the migration because all those duplicate subscriptions are
suddenly cancelled 'today'.

The migrator tool adds a ghost_migrated_to metadata field to the old
subscription. Ghost can use this to detect the old subscription and
delete the subscription and corresponding events.
2024-03-27 10:32:32 +01:00
Steve Larson
a1c4e64994
Added queueing middleware to handle high request volume (#19887)
ref https://linear.app/tryghost/issue/CFR-4/
- added request queueing middleware (express-queue) to handle high
request volume
- added new config option `optimization.requestQueue`
- added new config option `optimization.requestConcurrency`
- added logging of request queue depth - `req.queueDepth`

We've done a fair amount of investigation around improving Ghost's
resiliency to high request volume. While we believe this to be partly
due to database connection contention, it also seems Ghost gets
overwhelmed by the requests themselves. Implementing a simple queueing
system allows us a simple lever to change the volume of requests Ghost
is actually ingesting at any given time and gives us options besides
simply increasing database connection pool size.

---------

Co-authored-by: Michael Barrett <mike@ghost.org>
2024-03-21 09:25:07 -05:00
renovate[bot]
dfdd4e5cfa Update dependency express to v4.19.1 2024-03-21 11:50:48 +01:00
Daniel Lockyer
162f438c63 Updated @tryghost/errors dependency
- this version is written in TS, but was published a few months ago and
  needs to be bumped here
- also updates a previous deep include into the library, which was
  unnecessary anyway
2024-03-11 17:33:51 +01:00
renovate[bot]
3301332253 Update dependency express to v4.18.3 2024-03-07 13:42:27 +01:00
Ronald Langeveld
58c156001c
Added Newsletter Events Test to Members (#19653)
refs
https://linear.app/tryghost/issue/ENG-604/🐛-members-events-show-member-subscribed-to-archived-newsletter

- added a tests to avoid a potential regression
2024-02-05 13:54:10 +00:00
Ronald Langeveld
ddc1a58c84
🐛 Fixed members events for archived newsletters (#19638)
fixes
https://linear.app/tryghost/issue/ENG-604/🐛-members-events-show-member-subscribed-to-archived-newsletter

- This fixes a bug where it doesn't take archived newsletters into account and would
create an Event for subscribing back to those newsletters even though
its not the case, which causes some confusion for publishers in Member
Events and wastes rows in the DB.
2024-02-01 16:11:01 +02:00
Simon Backx
eb063f7a40
Fixed clearing invalid sender_email when changing newsletter sender_reply_to (#19555)
fixes PROD-102

When a newsletter has a sender_email stored in the database that Ghost
is not allowed to send from, we no longer return it as sender_email in
the API. Instead we return it as the sender_reply_to. That way the
expected behaviour is shown correctly in the frontend and the API result
also makes more sense.

In addition to that, when a change is made to a newsletters reply_to
address we'll clear any invalid sender_email values in that newsletter.
That makes sure we can clear the sender_reply_to value instead of
keeping the current fallback to sender_email if that one is stored.

On top of that, this change correclty updates the browse endpoint to use
the newsletter service instead of directly using the model.
2024-01-23 16:10:11 +01:00
Michael Barrett
ed0762fb51
Removed usage of yg when using NQL (#19287)
refs https://github.com/TryGhost/NQL/pull/73

The referenced PR removes `yg` from the parsed NQL output, so we also
need to remove any usage of it in Ghost
2024-01-15 14:40:01 +00:00
Simon Backx
e5f644c27f
🐛 Fixed contain/starts/endsWith filters with /, _ or % in them (#19015)
fixes GRO-25

Updated @tryghost/nql to 0.12.0 and other packages that depend on it

1. SQLite: when a filter string contains /.

When we use a NQL contain/starts/endsWith filter that contains a slash,
underlyingly the whole filter will get converted to a MongoDB query, in
which we just use a regexp to represent the filter. In here we will
escape the slash: \/ as expected in a regexp. Later when we convert this
MongoDB query back to knex/SQL, we use a SQL LIKE query. Currently we
don't remove the escaping here for a normal slash. MySQL seems to ignore
this (kinda incorrect). SQLite doesn't like it, and this breaks queries
on SQLite that use slashes. The solution here is simple: remove the
backslash escaping when converting the regexp to LIKE, just like we do
with other special regexp characters.

2. We don't escape % and _, which have a special meaning in LIKE queries

Usage of % and _ is now as expected and doesn't have the special SQL
meaning anymore.
2023-11-16 09:35:20 +00:00
Simon Backx
75bb53f065
🔒 Added support for logging out members on all devices (#18935)
fixes https://github.com/TryGhost/Product/issues/3738
https://www.notion.so/ghost/Member-Session-Invalidation-13254316f2244c34bcbc65c101eb5cc4

- Adds the transient_id column to the members table. This defaults to
email, to keep it backwards compatible (not logging out all existing
sessions)
- Instead of using the email in the cookies, we now use the transient_id
- Updating the transient_id means invalidating all sessions of a member
- Adds an endpoint to the admin api to log out a member from all devices
- Added the `all` body property to the DELETE session endpoint in the
members API. Setting it to true will sign a member out from all devices.
- Adds a UI button in Admin to sign a member out from all devices
- Portal 'sign out of all devices' will not be added for now

Related changes (added because these areas were affected by the code
changes):
- Adds a serializer to member events / activity feed endpoints - all
member fields were returned here, so the transient_id would also be
returned - which is not needed and bloats the API response size
(`transient_id` is not a secret because the cookies are signed)
- Removed `loadMemberSession` from public settings browse (not used
anymore + bad pattern)

Performance tests on site with 50.000 members (on Macbook M1 Pro):
- Migrate: 6s (adding column 4s, setting to email is 1s, dropping
nullable: 1s)
- Rollback: 2s
2023-11-15 17:10:28 +01:00
Simon Backx
370c6b465b
Filter members by email disabled (#18884)
fixes https://github.com/TryGhost/Product/issues/4108

- Updates filters behind a new alpha feature flag so you can also filter
on members who have email disabled (because the email had a permanent
bounce, they reported spam or the email address is invalid)
- When returning members, we now also use the email_disabled flag to set
email_suppression.suppressed correctly (in case they are out of sync,
which should normally never happen).
2023-11-14 14:37:01 +01:00
Simon Backx
14927ee24b
Added quotes to NQL filters with ids (#18958)
refs https://github.com/TryGhost/Product/issues/4120

Updated some places where we don't add quotes around ids in NQL filters,
which can be an issue when the id is a number
2023-11-13 12:00:20 +01:00
renovate[bot]
cc43a311c2 Update Types packages 2023-11-08 12:29:48 +01:00
Sag
b3c8055efe
Fixed email_disabled field after member update in Admin (#18827)
closes https://github.com/TryGhost/Product/issues/4046
- when editing the member's email in Admin, the email_disabled field was
not recalculated, making it inconsistent with the suppression list
- now, if the new email is part of the suppression list, we set
email_disabled to true. Otherwise set it to false
2023-11-02 17:15:03 +00:00
renovate[bot]
057d9599f5 Update TryGhost packages 2023-10-31 20:54:17 +01:00
renovate[bot]
df8eeb2249 Update Types packages 2023-10-31 14:40:32 +01:00
Michael Barrett
094ea1d2b0
🐛 Fixed plan upgrade not cancelling trial (#18699)
closes https://github.com/TryGhost/Product/issues/4036

Fixed a bug where a member on a trial plan would not have their trial
cancelled when they upgraded to a paid plan
2023-10-20 08:52:08 +01:00
Daniel Lockyer
85d41d0562 Aligned dependencies with resolution values
- this commit brings all dependencies up-to-date with the version set as
  a resolution
2023-10-13 08:37:36 +02:00
Daniel Lockyer
85098e07d4 Configured all unit tests to use dot reporter
refs https://ghost.slack.com/archives/C02G9E68C/p1696490748701419

- this configures mocha to use the dot reporter because the default is
  way too verbose in CI
2023-10-05 12:24:24 +02:00
renovate[bot]
d15de11bf3 Update dependency @types/node-jose to v1.1.11 2023-09-25 09:11:50 +02:00
renovate[bot]
a79717e9ba Update dependency @types/jsonwebtoken to v9.0.3 2023-09-18 08:51:04 +02:00
Princi Vershwal
f243083bfa
Name for custom newsletters (#18124)
refs https://github.com/TryGhost/Product/issues/3862 &
https://github.com/TryGhost/Product/issues/3863
2023-09-15 16:15:09 +05:30
Michael Barrett
a1f056ee86
🐛 Fixed portal showing incorrect expiry date for comped subscription (#18120)
refs https://github.com/TryGhost/Product/issues/3875

When a member had a comped subscription, the portal was showing an
incorrect expiry date. This was because the `expiry_date` was being set
to the `created_at` date of the subscription, rather than the
`expiry_date` of the comped subscription
2023-09-14 08:46:23 +01:00
Michael Barrett
72cc285184
Refactor validating specified newsletters in custom sign-up form (#18032)
refs https://github.com/TryGhost/Product/issues/3837

Moved the logic for validating specified newsletters to controller so
that the request can be failed
2023-09-08 13:55:02 +01:00
Princi Vershwal
f663774cf9
Added support for multiple newsletters in custom signup form (#18023)
refs https://github.com/TryGhost/Product/issues/3514

---------

Co-authored-by: Michael Barrett <mike182uk@gmail.com>
2023-09-08 11:09:44 +01:00
Princi Vershwal
83282ca4cd
Added checks for subscription to multiple newsletter through custom sign up form (#17994)
refs https://github.com/TryGhost/Product/issues/3810

---------

Co-authored-by: Michael Barrett <mike@ghost.org>
2023-09-07 18:27:32 +01:00
Daniel Lockyer
c6cb35074a Updated linting and testing packages 2023-09-01 15:51:17 +02:00