From daf5a41af0458d3097317810696127dab7b90140 Mon Sep 17 00:00:00 2001 From: Rishabh Garg Date: Sat, 13 Apr 2019 10:38:56 +0530 Subject: [PATCH] Added Admin API for deleting members (#10673) no issue - Added new API to delete members - Added methods to handle e2e member deletion - Deleting member via Admin leads to - Removal of member from payment processor and cancelling all active subscriptions immediately - Removal of member information from DB --- ghost/members-api/index.js | 2 ++ ghost/members-api/subscriptions/index.js | 18 +++++++++++++++++ .../payment-processors/stripe/api.js | 19 +++++++++++++++++- .../payment-processors/stripe/index.js | 20 +++++++++++++++++++ ghost/members-api/users.js | 19 ++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) diff --git a/ghost/members-api/index.js b/ghost/members-api/index.js index 65184b70da..7e28b3d04c 100644 --- a/ghost/members-api/index.js +++ b/ghost/members-api/index.js @@ -22,6 +22,7 @@ module.exports = function MembersApi({ validateMember, updateMember, getMember, + deleteMember, listMembers, sendEmail, siteConfig @@ -35,6 +36,7 @@ module.exports = function MembersApi({ createMember, updateMember, getMember, + deleteMember, validateMember, sendEmail, encodeToken, diff --git a/ghost/members-api/subscriptions/index.js b/ghost/members-api/subscriptions/index.js index d70e0cf019..b9be75cfdc 100644 --- a/ghost/members-api/subscriptions/index.js +++ b/ghost/members-api/subscriptions/index.js @@ -66,4 +66,22 @@ module.exports = class PaymentProcessorService { return this._processors[metadata.adapter].getSubscription(member, metadata); }); } + + removeSubscription(member, metadata) { + if (!metadata.adapter) { + return Promise.reject(new Error('removeSubscription(member, { adapter }) requires an adapter')); + } + return this._ready.then(() => { + return this._processors[metadata.adapter].removeSubscription(member, metadata); + }); + } + + removeCustomer(member, metadata) { + if (!metadata.adapter) { + return Promise.reject(new Error('removeCustomer(member, { adapter }) requires an adapter')); + } + return this._ready.then(() => { + return this._processors[metadata.adapter].removeCustomer(member, metadata); + }); + } }; diff --git a/ghost/members-api/subscriptions/payment-processors/stripe/api.js b/ghost/members-api/subscriptions/payment-processors/stripe/api.js index 7aa9cfcfec..fe3c15311b 100644 --- a/ghost/members-api/subscriptions/payment-processors/stripe/api.js +++ b/ghost/members-api/subscriptions/payment-processors/stripe/api.js @@ -118,6 +118,22 @@ function createCreator(resource, getAttrs) { }; } +function createRemover(resource, get, generateHashSeed) { + return function remove(stripe, object, ...rest) { + return get(stripe, object, generateHashSeed(object, ...rest)).then((res) => { + return stripe[resource].del(res.id).then((result) => { + return result; + }, (err) => { + throw err; + }); + }).catch((err) => { + if (err.code !== 'resource_missing') { + throw err; + } + }); + }; +} + function createEnsurer(get, create, generateHashSeed) { return function ensure(stripe, object, ...rest) { return get(stripe, object, generateHashSeed(object, ...rest)) @@ -134,9 +150,10 @@ function createEnsurer(get, create, generateHashSeed) { function createApi(resource, validResult, getAttrs, generateHashSeed) { const get = createGetter(resource, validResult); const create = createCreator(resource, getAttrs); + const remove = createRemover(resource, get, generateHashSeed); const ensure = createEnsurer(get, create, generateHashSeed); return { - get, create, ensure + get, create, remove, ensure }; } diff --git a/ghost/members-api/subscriptions/payment-processors/stripe/index.js b/ghost/members-api/subscriptions/payment-processors/stripe/index.js index 9024b55155..6c3b7b3517 100644 --- a/ghost/members-api/subscriptions/payment-processors/stripe/index.js +++ b/ghost/members-api/subscriptions/payment-processors/stripe/index.js @@ -91,4 +91,24 @@ module.exports = class StripePaymentProcessor { return api.subscriptions.get(this._stripe, member); }); } + + removeSubscription(member) { + if (!this._stripe) { + throw new Error('StripePaymentProcessor must be configured()'); + } + + return this._ready.then(() => { + return api.subscriptions.remove(this._stripe, member); + }); + } + + removeCustomer(member) { + if (!this._stripe) { + throw new Error('StripePaymentProcessor must be configured()'); + } + + return this._ready.then(() => { + return api.customers.remove(this._stripe, member); + }); + } }; diff --git a/ghost/members-api/users.js b/ghost/members-api/users.js index 3b32106a7e..7c1c23a86c 100644 --- a/ghost/members-api/users.js +++ b/ghost/members-api/users.js @@ -4,6 +4,7 @@ module.exports = function ({ updateMember, getMember, listMembers, + deleteMember, validateMember, sendEmail, encodeToken, @@ -45,12 +46,30 @@ module.exports = function ({ }); } + function destroy(...args) { + return getMember(...args).then((member) => { + if (!member) { + return null; + } + return subscriptions.getAdapters().then((adapters) => { + return Promise.all(adapters.map((adapter) => { + return subscriptions.removeCustomer(member, { + adapter + }); + })); + }).then(() => { + return deleteMember(...args); + }); + }); + } + return { requestPasswordReset, resetPassword, create: createMember, validate: validateMember, list: listMembers, + destroy, get }; };