mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
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.
210 lines
7.3 KiB
JavaScript
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;
|