Skip to content

Commit

Permalink
Added plan update option to stripe subscription update API (#154)
Browse files Browse the repository at this point in the history
no issue

- Current update stripe subscription API calls only allowed cancelling a plan
- This change adds option to pass plan's nickname as `planName` in request to update subscription to new plan
- Checks if plan name is valid and updates stripe subscription to new plan at default prorate behavior
  • Loading branch information
rishabhgrg committed May 19, 2020
1 parent 508daa5 commit b015a08
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 12 deletions.
31 changes: 22 additions & 9 deletions ghost/members-api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,15 @@ module.exports = function MembersApi({
middleware.updateSubscription.use(ensureStripe, body.json(), async function (req, res) {
const identity = req.body.identity;
const cancelAtPeriodEnd = req.body.cancel_at_period_end;
const planName = req.body.planName;
const subscriptionId = req.params.id;

let member;

try {
if (!identity) {
throw new common.errors.BadRequestError({
message: 'Cancel membership failed! Could not find member'
message: 'Updating subscription failed! Could not find member'
});
}

Expand All @@ -344,32 +345,44 @@ module.exports = function MembersApi({

if (!member) {
throw new common.errors.BadRequestError({
message: 'Cancel membership failed! Could not find member'
message: 'Updating subscription failed! Could not find member'
});
}
} catch (err) {
res.writeHead(401);
return res.end('Unauthorized');
}

// Don't allow removing subscriptions that don't belong to the member
const plan = planName && stripe.findPlanByNickname(planName);
if (planName && !plan) {
throw new common.errors.BadRequestError({
message: 'Updating subscription failed! Could not find plan'
});
}
const subscription = member.stripe.subscriptions.find(sub => sub.id === subscriptionId);

if (!subscription) {
res.writeHead(403);
return res.end('No permission');
}

if (cancelAtPeriodEnd === undefined) {
if (cancelAtPeriodEnd === undefined && planName === undefined) {
throw new common.errors.BadRequestError({
message: 'Canceling membership failed!',
help: 'Request should contain boolean "cancel" field.'
message: 'Updating subscription failed!',
help: 'Request should contain "cancel" or "plan" field.'
});
}
const subscriptionUpdate = {
id: subscription.id
};
if (cancelAtPeriodEnd !== undefined) {
subscriptionUpdate.cancel_at_period_end = !!(cancelAtPeriodEnd);
}

subscription.cancel_at_period_end = !!(cancelAtPeriodEnd);
if (plan) {
subscriptionUpdate.plan = plan.id;
}

await stripe.updateSubscriptionFromClient(subscription);
await stripe.updateSubscriptionFromClient(subscriptionUpdate);

res.writeHead(204);
res.end();
Expand Down
13 changes: 10 additions & 3 deletions ghost/members-api/lib/stripe/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const debug = require('ghost-ignition').debug('stripe');
const _ = require('lodash');
const {retrieve, list, create, update, del} = require('./api/stripeRequests');
const api = require('./api');

Expand Down Expand Up @@ -187,14 +188,20 @@ module.exports = class StripePaymentProcessor {
}

async updateSubscriptionFromClient(subscription) {
const updatedSubscription = await update(this._stripe, 'subscriptions', subscription.id, {
cancel_at_period_end: subscription.cancel_at_period_end
});
const updatedSubscription = await update(
this._stripe, 'subscriptions',
subscription.id,
_.pick(subscription, ['plan', 'cancel_at_period_end'])
);
await this._updateSubscription(updatedSubscription);

return updatedSubscription;
}

findPlanByNickname(nickname) {
return this._plans.find(plan => plan.nickname === nickname);
}

async getSubscriptions(member) {
const metadata = await this.storage.get(member);

Expand Down

0 comments on commit b015a08

Please sign in to comment.