no refs
The member id assigned when creating a new status event on member creation was incorrectly using `data.id` instead of `member.id`, which was undefined causing a validation error.
refs https://github.com/TryGhost/Ghost/issues/12602
* Added Event Repository
** Added method for MRR over time
** Added method for newsletter subscriptions over time
** Added method for gross volume over time
** Added method for status segment size over time
* Captured login events
* Captured newsletter subscription/unsubscription
* Captured email address change events
* Captured paid subscription events
* Captured payment events
* Captured status events
refs https://github.com/TryGhost/Team/issues/479
* Fixed updating payment method for canceled subscriptions
Stripe considers canceled subscriptions as non-existent, so any attempts
to update them will fail with a 404 not found. Prior to this change we
were attempting to update *all* subscriptions for a customer, including
those which were canceled. This would cause an error and the loop to
break.
* 🐛 Fixed errors for members with multiple active customers
Members with multiple active customers would have their first active
customer found updated with the new payment method. We would then
iterate through *all* active subscription, and attempt to update their
payment method. If a subscription was not owned by the customer that was
just updated, it would error and cause the loop to break out.
* Added ability to update a specific subscription's payment method
In order to remove ambiguity we add the ability to update the payment
method for a specific subscription. This will remove room for errors as
we will not have to worry about if a subscription belong to the customer
or not.
refs https://github.com/TryGhost/Team/issues/475
The subscription object here is a database model rather than an
ISubscription from the stripe library, and we need to 1) use the `get`
method to read attributes and 2) read the `subscription_id` attribute,
as the `id` is our internal one.
no-issue
Fetching relations via the model returns a promise, and this was missing
the `await` keyword. We also need to `get` the subscription_id attribute
as we're working with models rather than subscription objects.
no-issue
If we receive webhooks out of order, e.g. a
`customer.subscription.updated` with a status of 'active', followed by a
`customer.subscription.created` with a status of 'incomplete'. We would
overwrite the correct value with data from the "older" webhook. This
ensures that we always fetch the latest data from the Stripe API before
storing in the database.
no-issue
related to ef9cb0862c
In the last patch which fixes the bug for not passing custom redirect urls when a checkout session is created, we missed updating the case where a logged in member tries to update the subscription.
no issue
The logic to fetch member for a checkout session was incorrectly creating a new customer instead of finding an existing one, so the member's billing details was not getting updated.
no-issue
This refactors the members-api module so that it is easier to test going forward,
as well as easier to understand & navigate. The Stripe API no longer contains
storage code, this is all handled via the member repository. And we have dedicated
services for webhooks, and stripe plans initialisation.
refs https://github.com/TryGhost/Ghost/issues/12256 , https://github.com/TryGhost/Ghost/issues/12255
Currently when listing subscriptions for Members, we were only showing the subscriptions which have a status of trialing or active.
Based on discussion, the `unpaid` and `past_due` states on Stripe also represent owner's intention of considering a subscription as active instead of `cancelled`, so we allow any subscriptions under these 2 states to be also listed for a member and consider them as `paid`.
- Subscriptions will go into a past_due state if the payment is missed, this should be considered a grace period where the member still has access.
- After this the subscriptions will either go to the unpaid or the cancelled state - this can be configured on an account by account basis in the Stripe dashboard. `unpaid` is considered as an intention to keep the subscription to allow for re-activation later.
no-issue
Subscription created events are required for migrating Stripe subscriptions from
alternative platforms, which involves creating a new subscription for a customer
(outside of Ghost) before cancelling the original subscription.
refs https://github.com/TryGhost/Ghost/issues/12150
- `destroy` method was using incorrect cancel subscriptions method - stripe.cancelStripeSubscriptions - which doesn't exist
- Fixes call with intended method - `stripe.cancelAllSubscriptions` - to cancel all subscriptions
refs TryGhost/Ghost#12127
- Adds new `updateSubscription` method to members-api which allows updating individual subscription for a member
- New method only allows toggling of cancellation at period end for a subscription at the moment
no-issue
Using models internally and in the exported API means that we avoid expensive
`toJSON` calls, which affects performance when looping through large lists of
members. It also allows us to take advantage of the new relations used in the
models.
The addition of "ByID" methods for linking stripe customers and setting
complimentary subscriptions allows bulk imports to avoid the overhead of creating
a model for each members, instead passing an id string. n.b. currently the impl
_does_ still create models, but it makes it easier to optimise and refactor in the
future.
no-issue
Previously we would blindly put subscriptions into the database when we
received a webhook, which could result in orphaned rows that were not
linked to a customer (and by extension a member)
This updates the logic so that we will only add subscriptions if we have
a record of their customer.
Customers are only added during a checkout.session.completed webhook, at
which point a member is guarunteed, but for formailty and safety against
changes in the flow, the logic has been applied to inserting customers
too.
refs https://github.com/TryGhost/Ghost/issues/11557
If a subscription failed to delete, we would error and bailout of the
process, this updates it to log the error so that site owners have a
record of the error in the logs, but also to continue through the rest
of the subscriptions.
no-issue
Destroy is terminology we usually use for the model layer and was a
little confusing without context, this method is used in one place so
it's a low effort cleanup with minimal repercussions
refs https://github.com/TryGhost/Ghost/issues/12074
Some sites may have had duplicate webhooks created due to a race
condition. This updates the members-api to cleanup _all_ webhooks before
starting, allowing it to create webhooks on a fresh slate, and removing
possible causes of 401 errors due to incorrect webhook secrets.
refs https://github.com/TryGhost/Ghost/issues/12065
This protects us against multiple instances of the members-api being
started simultaneously and race conditions where inbetween the initial
"GET" of a plan which returns empty, and the "POST" of a plan to create
it, another instance has already created it.
refs https://github.com/TryGhost/Ghost/issues/12061
Due to a bug in Ghost webhooks are now created with a trailing "/" which
meant that the previous webhooks to that (without a slash) was never
removed.
This results in users receiving emails from stripe about failed webhook
delivery, which is not good at all.
This fix lists out the webhooks and finds (if present) the webhook which
matches the current URL, minus the trailing slash. If found it will then
attempt to delete that webhook thus stopping the emails from Stripe.
I've added a note to remove this code as it should only ever need to run
once, and can be removed for the Ghost release after these changes.
no-issue
* Refactored model dependencies
This groups all of the model depenencies into a single models object,
and renames the models with more concise identifiers
* Fixed spacing
* Added webhook support to metadata
* Refactored stripe configure to have better logging
* Refactored webhook creation to reuse existing webhook
* Installed @types/stripe
- We have many customers asking for INR as there are special rules in Stripe for this currency
- As well as a desire for local-selling
- Meaning it's not valid to use e.g. USD instead
no issue
- This method is needed to be able to validate if customer exist in configured Stripe account before attempting to link one with local member.