refs https://github.com/TryGhost/Team/issues/1808
refs https://github.com/TryGhost/Team/issues/1809
refs https://github.com/TryGhost/Team/issues/1820
refs https://github.com/TryGhost/Team/issues/1814
### Changes in `member-events` package
- Added MemberCreatedEvent (event, not model)
- Added SubscriptionCreatedEvent (event, not model)
### Added `member-attribution` package (new)
- Added the AttributionBuilder class which is able to convert a url history to an attribution object (exposed as getAttribution on the service itself, which handles the dependencies)
```
[{
"path": "/",
"time": 123
}]
```
to
```
{
"url": "/",
"id": null,
"type": "url"
}
```
- event handler listens for MemberCreatedEvent and SubscriptionCreatedEvent and creates the corresponding models in the database.
### Changes in `members-api` package
- Added urlHistory to `sendMagicLink` endpoint body + convert the urlHistory to an attribution object that is stored in the tokenData of the magic link (sent by Portal in this PR: https://github.com/TryGhost/Portal/pull/256).
- Added urlHistory to `createCheckoutSession` endpoint + convert the urlHistory to attribution keys that are saved in the Stripe Session metadata (sent by Portal in this PR: https://github.com/TryGhost/Portal/pull/256).
- Added attribution data property to member repository's create method (when a member is created)
- Dispatch MemberCreatedEvent with attribution
### Changes in `members-stripe-service` package (`ghost/stripe`)
- Dispatch SubscriptionCreatedEvent in WebhookController on subscription checkout (with attribution from session metadata)
refs https://github.com/TryGhost/Team/issues/1726
Free trial offers don't have a Stripe coupon created for them, as the trial is directly added to checkout session. So for mapping a subscription to offer, we pass the offer id directly from checkout metadata to link the subscription in backend with right offer data. This also handles the case where the offer id against a subscription can get overwritten for a subsequent subscription event, as the sub event from Stripe doesn't has the trial offer info.
- handles storing an offer id for a subscription
- updates member detail in Admin to show the offer info for a subscription
refs https://github.com/TryGhost/Team/issues/1726
- free trial offers don't need a stripe coupon created for them
- checkout sessions for free trial offers ignore stripe coupon and directly pass the trial days value
- trial days of an offer take precedence over trial days added as default to a tier
Without this check, an inactive price in our database will just be
reactivated each time it is required. This can cause issues when
prices have been deleted.
By adding this constraint to the query, we will create a new price in
Stripe and our database when attempting to use an inactive price, this
is particularly useful when trying to fix problems caused by Stripe
prices being deleted.
refs https://github.com/TryGhost/Team/issues/1724
With free trials, members can start subscriptions with a trial period. This change stores the information about trial start and end date for every subscription so it can be shown on Admin/Portal for member.
- adds new `trial_start_at` column for storing trial start date on Stripe subscription. Will in most cases match the start of subscription date.
- adds new `trial_end_at` column for storing trial end date on Stripe subscription.
- wires storing trial start and end values on stripe subscription
refs https://github.com/TryGhost/Team/issues/1724
- wires trial days stored on a tier to stripe checkout session creation
- removes deprecated `trial_from_plan` if trial days is set
refs https://github.com/TryGhost/Team/issues/1716
- Adds the bio field to the API output
- Allow setting bio when updating the member
- Includes new E2E tests for the members API that were missing
refs https://github.com/TryGhost/Team/issues/1709
- New event type `comment_event` (comments and replies of a member in the activity feed)
- Includes member, post and parent relation by default
- Added new output mapper for ActivityFeed events
**Changes to `Comment` model:**
* **Only limit comment fetched to root comments when not authenticated as a user:**
`enforcedFilters` is applied to all queries, which is a problem because for the activity feed we also need to fetch comments which have a parent_id that is not null (`Member x replied to a comment`). The current filter in the model is specifically for the members API, not the admin API (so checking the user should fix that, not sure if that is a good pattern but couldn’t find a better alternative).
* **Only set default relations for comments when withRelated is empty or not set:**
`defaultRelations`: Right now, for every fetch it would force all these relations. But we don’t need all those relations for the activity feed; So I updated the pattern to only set the default relations when it is empty (which we also do on a couple of other places and seems like a good pattern). I also updated the comments-ui frontend to not send ?include
refs https://github.com/TryGhost/Team/issues/1717
- Updates last_commented_at and last_seen_at (only once a day)
- Used the LastSeenAtUpdater, so we can combine updating last_commented_at and last_seen_at in one query + used same pattern
- Updated comments service to await emails in order to make E2E tests more stable (as we don't have any method to await emails and test emails otherwise). This removed the email sending logic from the `onCreated` hook of the model.
refs https://github.com/TryGhost/Team/issues/1174
This paves the way for Ghost to be able to redirect to the referrer
page when dealign with signup magic links. We pass the referrer for
all types of magic links however, to allow extension of this
functionality in the future.
We've also removed the concept of `requestSrc` which has been unused
for a while now.
refs https://github.com/TryGhost/Team/issues/1674
- While preparing the changes had a look around and made small refactors to understand the codebase a little better. In general it's best to keep the method parameters as small and precise as possible instead of passing around a "bag-of-all-the-things" like "data" around
refs https://github.com/TryGhost/Team/issues/1674
- While preparing the changes had a look around and made small refactors to understand the codebase a little better. In general it's best to keep the method parameters as small and precise as possible instead of passing around a "bag-of-all-the-things" like "data" around
refs https://github.com/TryGhost/Team/issues/1526
We weren't using the `req.body.customerEmail` to load a member and
check their existing tiers, this meant that existing members which
were signed out and attempted to create a stripe checkout session were
able to.
refs https://ghost.slack.com/archives/C02G9E68C/p1652980792270029
When bulk unsubscribing members, the number of deleted newsletter relations are returned instead of the number of members with newsletters that were cleared. This update deletes newsletter relations on member_id, so we can return the count of members instead of newsletter relations that were deleted.
Tests in https://github.com/TryGhost/Ghost/pull/14871
- In case of an offer id present in stripe checkout session, the cadence and tier id values can be null
- this fixes the invalid 400 thrown on missing cadence value while creating stripe checkout session for offer
no issue
- Setting a dedicated source for imported members was already done with 455778662c
- This unifies the same source but keeps the usage of context setting on init alive
refs https://github.com/TryGhost/Team/issues/1577
The call to `edit` was not loading the newsletter relations which is needed
by the serializer used by the webhooks service.
Co-authored-by: Fabien "egg" O'Carroll <fabien@allou.is>
no issue
Using a designated source for members being added within the `importer` context will better describe the source in our members event table. It's more consistent with the usage of the other possible sources.
no issue
When importing members, the members-importer isn't aware of the context and therefore falls back to use `member` as a source in members event table. This makes it impossible to determine imported members from others.
- Added an `context` property to the options in the members-importer constructor
- Checked for `importer` context when creating member events and assigned the source `admin` to it
refs https://github.com/TryGhost/Team/issues/1478
- When creating or editing a member, we'll add the newsletter ids to the subscribe events
- When susbcribing to multiple newsletters, we'll create multiple subscribe events
- When removing newsletters from a member, we'll create one or more subscribe events
- Tests in https://github.com/TryGhost/Ghost/pull/14579
refs https://github.com/TryGhost/Team/issues/1520
- Instead of doing the matching of the offers and subscriptions by looking at the offer redemptions, we can now look at the offer_id from subscriptions.
- This also fixes an issue where we don't attach the offer object to subscriptions in the members' browse method
- Updated browse behaviour to match the read behaviour of members (product relation needs to get loaded because it is missing in member.products if the subscription is expired).
Tests in https://github.com/TryGhost/Ghost/pull/14515
refs https://github.com/TryGhost/Team/issues/1519
- Removed offer metadata again from offer (added in one of the previous commits)
- Added `getByStripeCouponId` method in offer repository (required to find an offer based on the stripe_coupon_id)
- Match discounts from Stripe based on the stripe_coupon_id instead of metadata
refs https://github.com/TryGhost/Team/issues/1519
- Added offer repository dependency to member repository (offerAPI didn't work because it creates a new transaction that resulted in a deadlock during tests)
- Store the offer id from the Stripe subscription metadata in the subscription (only if the discount is still active)
- Also added the offer id to the metadata for a Stripe coupon, this will make adding and removing coupons a bit more foolproof
- Prefer the usage of the offer metadata from a coupon if it is present
- When no discount is applied to a subscription, it always sets the offer id to null, even when the metadata still contains the offer
- The offer_id remains stored when a subscription is canceled/expired
refs https://github.com/TryGhost/Team/issues/1469
Previously, members were subscribed to all available newsletters by default when added. This change updates the default newsletters subscription for member to take into account newsletter preferences for auto opt-in as well as visibility.
refs: https://github.com/TryGhost/Toolbox/issues/293
Things needed to create this:
* MemberSubscriptionEvent now has an import source
* Importer now creates events with this type
* Verification trigger logic changed to use 30 day window of imports
* Updated member mrr_delta calculation to use stored mrr
refs https://github.com/TryGhost/Team/issues/1456
Simplifies the calculation of MRR deltas, which will make it easier to update MRR to include offers and cancellations in the future.
* Adjusted MRR and MRR delta calculation to consider "forever" duration offers
refs https://github.com/TryGhost/Team/issues/1451
Uses the discount information from Stripe to calculate the MRR (this was the easiest way to include it + also supports manually created discounts from users)
refs https://github.com/TryGhost/Team/issues/1490
With multiple newsletters, free members can choose their newsletter subscription preference while signing up.
This change -
- includes newsletters data in magic link token creation
- attaches newsletter data to new members created via magic link
refs https://github.com/TryGhost/Team/issues/1456
- Sets the `mrr` column correctly when updating a subscription
- We can use this value in the future to have an improved calculation of mrr_delta, but we first need to have the migration in place
- Updated missing cancelled -> canceled