0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00
ghost/core/server/services/members/config.js
Fabien O'Carroll 44f1c0a7b6 Updated members config to respect stripeDirect
no-issue

If the stripeDirect config value is NOT set / false, then connect token
should be used over the API keys if both are present, or else use
whichever is present.  If the stripeDirect config value is set /
true,then API keys should be used and stripe connect token should be
completely ignored.

The reason for this is that we want to use Stripe Connect as the primary
auth method going forward, but we want to keep the direct version
availiable should the Ghost Foundation ever dismantle. This will allow
people to use all the same features with no dependency on an external
service.
2020-06-02 15:28:42 +02:00

210 lines
7.3 KiB
JavaScript

const {URL} = require('url');
const crypto = require('crypto');
const path = require('path');
const COMPLIMENTARY_PLAN = {
name: 'Complimentary',
currency: 'usd',
interval: 'year',
amount: '0'
};
class MembersConfigProvider {
/**
* @param {object} options
* @param {{get: (key: string) => any}} options.settingsCache
* @param {{get: (key: string) => any}} options.config
* @param {any} options.urlUtils
* @param {any} options.logging
* @param {{original: string}} options.ghostVersion
*/
constructor(options) {
this._settingsCache = options.settingsCache;
this._config = options.config;
this._urlUtils = options.urlUtils;
this._logging = options.logging;
this._ghostVersion = options.ghostVersion;
}
/**
* @private
*/
_getDomain() {
const domain = this._urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
return domain && domain[1];
}
/**
*/
getEmailFromAddress() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings') || {};
return `${subscriptionSettings.fromAddress || 'noreply'}@${this._getDomain()}`;
}
getPublicPlans() {
const CURRENCY_SYMBOLS = {
USD: '$',
AUD: '$',
CAD: '$',
GBP: '£',
EUR: '€'
};
const defaultPriceData = {
monthly: 0,
yearly: 0
};
try {
const membersSettings = this._settingsCache.get('members_subscription_settings');
const stripeProcessor = membersSettings.paymentProcessors.find(
processor => processor.adapter === 'stripe'
);
const priceData = stripeProcessor.config.plans.reduce((prices, plan) => {
const numberAmount = 0 + plan.amount;
const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
return Object.assign(prices, {
[plan.name.toLowerCase()]: dollarAmount
});
}, {});
priceData.currency = String.prototype.toUpperCase.call(stripeProcessor.config.currency || 'usd');
priceData.currency_symbol = CURRENCY_SYMBOLS[priceData.currency];
if (Number.isInteger(priceData.monthly) && Number.isInteger(priceData.yearly)) {
return priceData;
}
return defaultPriceData;
} catch (err) {
return defaultPriceData;
}
}
/**
* @function getStripeAPIKeys
* @desc Gets the stripe api keys from settings, respecting the stripeDirect config
*
* @param {string} publicKey - The publicKey to use if stripeDirect is enabled
* @param {string} secretKey - The secretKey to use if stripeDirect is enabled
*
* @returns {{publicKey: string, secretKey: string}}
*/
getStripeAPIKeys(publicKey, secretKey) {
const stripeDirect = this._config.get('stripeDirect');
const stripeConnectIntegration = this._settingsCache.get('stripe_connect_integration');
const hasStripeConnectKeys = stripeConnectIntegration.secret_key && stripeConnectIntegration.public_key;
if (stripeDirect || !hasStripeConnectKeys) {
return {
publicKey,
secretKey
};
}
return {
publicKey: stripeConnectIntegration.public_key,
secretKey: stripeConnectIntegration.secret_key
};
}
getStripePaymentConfig() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings');
const stripePaymentProcessor = subscriptionSettings.paymentProcessors.find(
paymentProcessor => paymentProcessor.adapter === 'stripe'
);
if (!stripePaymentProcessor || !stripePaymentProcessor.config) {
return null;
}
if (!stripePaymentProcessor.config.public_token || !stripePaymentProcessor.config.secret_token) {
return null;
}
// NOTE: "Complimentary" plan has to be first in the queue so it is created even if regular plans are not configured
stripePaymentProcessor.config.plans.unshift(COMPLIMENTARY_PLAN);
const siteUrl = this._urlUtils.getSiteUrl();
const webhookHandlerUrl = new URL('/members/webhooks/stripe', siteUrl);
const checkoutSuccessUrl = new URL(siteUrl);
checkoutSuccessUrl.searchParams.set('stripe', 'success');
const checkoutCancelUrl = new URL(siteUrl);
checkoutCancelUrl.searchParams.set('stripe', 'cancel');
const billingSuccessUrl = new URL(siteUrl);
billingSuccessUrl.searchParams.set('stripe', 'billing-update-success');
const billingCancelUrl = new URL(siteUrl);
billingCancelUrl.searchParams.set('stripe', 'billing-update-cancel');
const stripeApiKeys = this.getStripeAPIKeys(
stripePaymentProcessor.config.public_token,
stripePaymentProcessor.config.secret_token
);
return {
publicKey: stripeApiKeys.publicKey,
secretKey: stripeApiKeys.secretKey,
checkoutSuccessUrl: checkoutSuccessUrl.href,
checkoutCancelUrl: checkoutCancelUrl.href,
billingSuccessUrl: billingSuccessUrl.href,
billingCancelUrl: billingCancelUrl.href,
webhookHandlerUrl: webhookHandlerUrl.href,
product: stripePaymentProcessor.config.product,
plans: stripePaymentProcessor.config.plans,
appInfo: {
name: 'Ghost',
partner_id: 'pp_partner_DKmRVtTs4j9pwZ',
version: this._ghostVersion.original,
url: 'https://ghost.org/'
}
};
}
getAuthSecret() {
const hexSecret = this._settingsCache.get('members_email_auth_secret');
if (!hexSecret) {
this._logging.warn('Could not find members_email_auth_secret, using dynamically generated secret');
return crypto.randomBytes(64);
}
const secret = Buffer.from(hexSecret, 'hex');
if (secret.length < 64) {
this._logging.warn('members_email_auth_secret not large enough (64 bytes), using dynamically generated secret');
return crypto.randomBytes(64);
}
return secret;
}
getAllowSelfSignup() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings');
return subscriptionSettings.allowSelfSignup;
}
getTokenConfig() {
const {href: membersApiUrl} = new URL(
this._urlUtils.getApiPath({version: 'v3', type: 'members'}),
this._urlUtils.urlFor('admin', true)
);
return {
issuer: membersApiUrl,
publicKey: this._settingsCache.get('members_public_key'),
privateKey: this._settingsCache.get('members_private_key')
};
}
getSigninURL(token, type) {
const siteUrl = this._urlUtils.getSiteUrl();
const signinURL = new URL(siteUrl);
signinURL.pathname = path.join(signinURL.pathname, '/members/');
signinURL.searchParams.set('token', token);
signinURL.searchParams.set('action', type);
return signinURL.href;
}
}
module.exports = MembersConfigProvider;