Removed lib/subscriptions

no-issue

This is no longer used
This commit is contained in:
Fabien O'Carroll 2019-09-06 13:07:35 +08:00
parent 1c5ba6056a
commit 7376a333c2
10 changed files with 0 additions and 481 deletions

View File

@ -1,90 +0,0 @@
const stripe = require('./payment-processors/stripe');
const adapters = {
stripe
};
module.exports = class PaymentProcessorService {
constructor(config) {
this._ready = new Promise((resolve, reject) => {
this._resolveReady = resolve;
this._rejectReady = reject;
});
process.nextTick(() => this.configure(config));
}
configure({processors}) {
this._processors = {};
Promise.all(processors.map(({
adapter,
config
}) => {
this._processors[adapter] = new adapters[adapter];
return this._processors[adapter].configure(config);
})).then(() => {
return Object.keys(this._processors);
}).then(this._resolveReady, this._rejectReady);
return this._ready;
}
getAdapters() {
return this._ready;
}
getConfig(adapter) {
if (!adapter) {
return Promise.reject(new Error('getConfig(adapter) requires an adapter'));
}
return this._ready.then(() => {
return this._processors[adapter].getConfig();
});
}
getPublicConfig(adapter) {
if (!adapter) {
return Promise.reject(new Error('getPublicConfig(adapter) requires an adapter'));
}
return this._ready.then(() => {
return this._processors[adapter].getPublicConfig();
});
}
createSubscription(member, metadata) {
if (!metadata.adapter) {
return Promise.reject(new Error('createSubscription(member, { adapter }) requires an adapter'));
}
return this._ready.then(() => {
return this._processors[metadata.adapter].createSubscription(member, metadata);
});
}
getSubscription(member, metadata) {
if (!metadata.adapter) {
return Promise.reject(new Error('getSubscription(member, { adapter }) requires an adapter'));
}
return this._ready.then(() => {
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);
});
}
};

View File

@ -1,75 +0,0 @@
const hash = data => require('crypto').createHash('sha256').update(data).digest('hex');
const {
del: stripeDel,
create: stripeCreate,
retrieve: stripeRetrieve
} = require('./stripeRequests');
function createDeterministicApi(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, remove, ensure
};
}
function prefixHashSeed(stripe, seed) {
const prefix = stripe.__TEST_MODE__ ? 'test_' : 'prod_';
return prefix + seed;
}
function createGetter(resource, validResult) {
return function get(stripe, object, idSeed) {
const id = hash(prefixHashSeed(stripe, idSeed));
return stripeRetrieve(stripe, resource, id)
.then((result) => {
if (validResult(result)) {
return result;
}
return get(stripe, object, id);
}, (err) => {
err.id_requested = id;
throw err;
});
};
}
function createCreator(resource, getAttrs) {
return function create(stripe, id, object, ...rest) {
return stripeCreate(
stripe,
resource,
Object.assign(getAttrs(object, ...rest), {id})
);
};
}
function createRemover(resource, get, generateHashSeed) {
return function remove(stripe, object, ...rest) {
return get(stripe, object, generateHashSeed(object, ...rest)).then((res) => {
return stripeDel(stripe, resource, res.id);
}).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))
.catch((err) => {
if (err.code !== 'resource_missing') {
throw err;
}
const id = err.id_requested;
return create(stripe, id, object, ...rest);
});
};
}
module.exports = createDeterministicApi;

View File

@ -1,56 +0,0 @@
module.exports = function createStripeRequest(makeRequest) {
return function stripeRequest(...args) {
const errorHandler = (err) => {
switch (err.type) {
case 'StripeCardError':
// Card declined
throw err;
case 'RateLimitError':
// Ronseal
return exponentiallyBackoff(makeRequest, ...args).catch((err) => {
// We do not want to recurse further if we get RateLimitError
// after running the exponential backoff
if (err.type === 'RateLimitError') {
throw err;
}
return errorHandler(err);
});
case 'StripeInvalidRequestError':
// Invalid params to the request
throw err;
case 'StripeAPIError':
// Rare internal server error from stripe
throw err;
case 'StripeConnectionError':
// Weird network/https issue
throw err;
case 'StripeAuthenticationError':
// Invalid API Key (probably)
throw err;
default:
throw err;
}
};
return makeRequest(...args).catch(errorHandler);
};
};
function exponentiallyBackoff(makeRequest, ...args) {
function backoffRequest(timeout, ...args) {
return new Promise(resolve => setTimeout(resolve, timeout)).then(() => {
return makeRequest(...args).catch((err) => {
if (err.type !== 'RateLimitError') {
throw err;
}
if (timeout > 30000) {
throw err;
}
return backoffRequest(timeout * 2, ...args);
});
});
}
return backoffRequest(1000, ...args);
}

View File

@ -1,12 +0,0 @@
const createDeterministicApi = require('./createDeterministicApi');
const isNotDeleted = x => !x.deleted;
const getCustomerAttr = ({email}) => ({email});
const getCustomerHashSeed = member => member.email;
module.exports = createDeterministicApi(
'customers',
isNotDeleted,
getCustomerAttr,
getCustomerHashSeed
);

View File

@ -1,6 +0,0 @@
module.exports = {
customers: require('./customers'),
products: require('./products'),
plans: require('./plans'),
subscriptions: require('./subscriptions')
};

View File

@ -1,21 +0,0 @@
const createDeterministicApi = require('./createDeterministicApi');
const isActive = x => x.active;
const getPlanAttr = ({name, amount, interval, currency}, product) => ({
nickname: name,
amount,
interval,
currency,
product: product.id,
billing_scheme: 'per_unit'
});
const getPlanHashSeed = (plan, product) => {
return product.id + plan.interval + plan.currency + plan.amount;
};
module.exports = createDeterministicApi(
'plans',
isActive,
getPlanAttr,
getPlanHashSeed
);

View File

@ -1,12 +0,0 @@
const createDeterministicApi = require('./createDeterministicApi');
const isActive = x => x.active;
const getProductAttr = ({name}) => ({name, type: 'service'});
const getProductHashSeed = () => 'Ghost Subscription';
module.exports = createDeterministicApi(
'products',
isActive,
getProductAttr,
getProductHashSeed
);

View File

@ -1,26 +0,0 @@
const createStripeRequest = require('./createStripeRequest');
const createSource = createStripeRequest(function (stripe, customerId, stripeToken) {
return stripe.customers.createSource(customerId, {
source: stripeToken
});
});
const retrieve = createStripeRequest(function (stripe, resource, id) {
return stripe[resource].retrieve(id);
});
const create = createStripeRequest(function (stripe, resource, object) {
return stripe[resource].create(object);
});
const del = createStripeRequest(function (stripe, resource, id) {
return stripe[resource].del(id);
});
module.exports = {
createSource,
retrieve,
create,
del
};

View File

@ -1,64 +0,0 @@
const customers = require('./customers');
const {del, create, createSource} = require('./stripeRequests');
function removeSubscription(stripe, member) {
return customers.get(stripe, member, member.email).then((customer) => {
// CASE customer has no subscriptions
if (!customer.subscriptions || customer.subscriptions.total_count === 0) {
throw new Error('Cannot remove subscription');
}
const subscription = customer.subscriptions.data[0];
return del(stripe, 'subscriptions', subscription.id);
});
}
function getSubscription(stripe, member) {
return customers.get(stripe, member, member.email).then((customer) => {
// CASE customer has either none or multiple subscriptions
if (!customer.subscriptions || customer.subscriptions.total_count !== 1) {
return {};
}
const subscription = customer.subscriptions.data[0];
// CASE subscription has multiple plans
if (subscription.items.total_count !== 1) {
return {};
}
const plan = subscription.plan;
return {
validUntil: subscription.current_period_end,
plan: plan.nickname,
amount: plan.amount,
status: subscription.status
};
}).catch(() => {
return {};
});
}
function createSubscription(stripe, member, metadata) {
return customers.ensure(stripe, member, member.email).then((customer) => {
if (customer.subscriptions && customer.subscriptions.total_count !== 0) {
throw new Error('Customer already has a subscription');
}
return createSource(stripe, customer.id, metadata.stripeToken).then(() => {
return create(stripe, 'subscriptions', {
customer: customer.id,
items: [{plan: metadata.plan.id}],
coupon: metadata.coupon
});
});
});
}
module.exports = {
create: createSubscription,
get: getSubscription,
remove: removeSubscription
};

View File

@ -1,119 +0,0 @@
const api = require('./api');
module.exports = class StripePaymentProcessor {
constructor() {
this._ready = new Promise((resolve, reject) => {
this._resolveReady = resolve;
this._rejectReady = reject;
});
}
configure(config) {
const stripe = require('stripe')(config.secret_token);
stripe.__TEST_MODE__ = config.secret_token.startsWith('sk_test_');
api.products.ensure(stripe, config.product).then((product) => {
return Promise.all(
config.plans.map(plan => api.plans.ensure(stripe, plan, product))
).then((plans) => {
this._stripe = stripe;
this._product = product;
this._plans = plans;
this._public_token = config.public_token;
return {
product,
plans
};
});
}).then(this._resolveReady, this._rejectReady);
return this._ready;
}
getConfig() {
if (!this._plans) {
throw new Error('StripePaymentProcessor must be configured()');
}
return this._ready.then(() => {
return this._plans;
});
}
getPublicConfig() {
if (!this._plans) {
throw new Error('StripePaymentProcessor must be configured()');
}
return this._ready.then(() => {
return {
adapter: 'stripe',
config: {
publicKey: this._public_token,
plans: this._plans.map(({id, currency, amount, interval, nickname}) => ({
id, currency, amount, interval,
name: nickname
}))
}
};
});
}
createSubscription(member, metadata) {
if (!this._stripe) {
throw new Error('StripePaymentProcessor must be configured()');
}
if (!metadata.stripeToken) {
throw new Error('createSubscription(member, {stripeToken}) missing stripeToken');
}
if (!metadata.plan) {
throw new Error('createSubscription(member, {plan}) missing plan');
}
return this._ready.then(() => {
const plan = this._plans.find(plan => plan.nickname === metadata.plan);
if (!plan) {
throw new Error('Unknown plan');
}
return api.subscriptions.create(this._stripe, member, {
plan,
stripeToken: metadata.stripeToken,
coupon: metadata.coupon
});
});
}
getSubscription(member) {
if (!this._stripe) {
throw new Error('StripePaymentProcessor must be configured()');
}
return this._ready.then(() => {
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);
});
}
};