🐛 Fixed issues when updating payment method (#247)

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.
This commit is contained in:
Fabien 'egg' O'Carroll 2021-02-23 11:19:21 +00:00 committed by GitHub
parent 0d97bd7dd6
commit 2fb6708944
2 changed files with 40 additions and 6 deletions

View File

@ -172,11 +172,27 @@ module.exports = class RouterController {
res.writeHead(403);
return res.end('Bad Request.');
}
const customer = await this._stripeAPIService.getCustomerForMemberCheckoutSession(member);
let customer;
if (!req.body.subscription_id) {
customer = await this._stripeAPIService.getCustomerForMemberCheckoutSession(member);
} else {
const subscriptions = await member.related('stripeSubscriptions').fetch();
const subscription = subscriptions.models.find((sub) => {
return sub.get('subscription_id') === req.body.subscription_id;
});
if (!subscription) {
res.writeHead(404);
res.end(`Could not find subscription ${req.body.subscription_id}`);
}
customer = await this._stripeAPIService.getCustomer(subscription.get('customer_id'));
}
const session = await this._stripeAPIService.createCheckoutSetupSession(customer, {
successUrl: req.body.successUrl || this._config.billingSuccessUrl,
cancelUrl: req.body.cancelUrl || this._config.billingCancelUrl
cancelUrl: req.body.cancelUrl || this._config.billingCancelUrl,
subscription_id: req.body.subscription_id
});
const publicKey = this._stripeAPIService.getPublicKey();
const sessionInfo = {

View File

@ -157,17 +157,35 @@ module.exports = class StripeWebhookService {
setupIntent.payment_method
);
const subscriptions = await member.related('stripeSubscriptions').fetch();
for (const subscription of subscriptions.models) {
if (setupIntent.metadata.subscription_id) {
const updatedSubscription = await this._stripeAPIService.updateSubscriptionDefaultPaymentMethod(
subscription.get('subscription_id'),
setupIntent.metadata.subscription_id,
setupIntent.payment_method
);
await this._memberRepository.linkSubscription({
id: member.id,
subscription: updatedSubscription
});
return;
}
const subscriptions = await member.related('stripeSubscriptions').fetch();
const activeSubscriptions = subscriptions.models.filter((subscription) => {
return ['active', 'trialing', 'unpaid', 'past_due'].includes(subscription.get('status'));
});
for (const subscription of activeSubscriptions) {
if (subscription.get('customer_id') === setupIntent.metadata.customer_id) {
const updatedSubscription = await this._stripeAPIService.updateSubscriptionDefaultPaymentMethod(
subscription.get('subscription_id'),
setupIntent.payment_method
);
await this._memberRepository.linkSubscription({
id: member.id,
subscription: updatedSubscription
});
}
}
}