mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
no-issue This was initially missed as local settings always had the original stripeDirect keys, this ensures that regardless of Connect vs Direct vs Both vs Neither that the config is correct. Also ensures that the Members API instance is reloaded when the Stripe Connect settings are changed.
221 lines
7.7 KiB
JavaScript
221 lines
7.7 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') || {};
|
|
const fromAddress = subscriptionSettings.fromAddress || 'noreply';
|
|
|
|
// Any fromAddress without domain uses site domain, like default setting `noreply`
|
|
if (fromAddress.indexOf('@') < 0) {
|
|
return `${fromAddress}@${this._getDomain()}`;
|
|
}
|
|
return fromAddress;
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
isStripeConnected() {
|
|
const paymentConfig = this.getStripePaymentConfig();
|
|
|
|
return (paymentConfig && paymentConfig.publicKey && paymentConfig.secretKey);
|
|
}
|
|
|
|
getStripePaymentConfig() {
|
|
const subscriptionSettings = this._settingsCache.get('members_subscription_settings');
|
|
|
|
const stripePaymentProcessor = subscriptionSettings.paymentProcessors.find(
|
|
paymentProcessor => paymentProcessor.adapter === 'stripe'
|
|
);
|
|
|
|
if (!stripePaymentProcessor || !stripePaymentProcessor.config) {
|
|
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
|
|
);
|
|
|
|
if (!stripeApiKeys.publicKey || !stripeApiKeys.secretKey) {
|
|
return null;
|
|
}
|
|
|
|
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;
|