0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-15 03:01:37 -05:00

Cleaned up members & stripe settings (#11957)

* Updated members default settings

ref #10318

This pulls out the members_subscription_settings & stripe_connect_intgration settings into separate keys

* Updated usage of members_from_address

* Updated stripe_connect usage

* Updated members config to use new settings

* Updated members middleware to use isStripeConnected

* Updated members service to reload correctly

We reload the members-api instance when the related settings change, so
this makes sure we're listening to the correct settings changes

* Updated ghost_head helper to use new settings

* Updated theme middleware to use new settings

* Renamed members_allow_signup -> members_allow_free_signup

* Fixed tests after settings refactor

* Removed  from direct key settings key

* Fixed regression tests for settings api
This commit is contained in:
Fabien 'egg' O'Carroll 2020-06-29 14:22:42 +00:00 committed by GitHub
parent 3aa68b95e7
commit ee786aaa5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 313 additions and 247 deletions

View file

@ -37,19 +37,15 @@ function finaliseStructuredData(metaData) {
}
function getMembersHelper() {
const stripePaymentProcessor = settingsCache.get('members_subscription_settings').paymentProcessors.find(
paymentProcessor => paymentProcessor.adapter === 'stripe'
);
const stripeSecretToken = stripePaymentProcessor.config.secret_token;
const stripePublicToken = stripePaymentProcessor.config.public_token;
const stripeConnectIntegration = settingsCache.get('stripe_connect_integration');
const stripeDirectSecretKey = settingsCache.get('stripe_secret_key');
const stripeDirectPublishableKey = settingsCache.get('stripe_publishable_key');
const stripeConnectAccountId = settingsCache.get('stripe_connect_account_id');
let membersHelper = `<script defer src="${getAssetUrl('public/members.js', true)}"></script>`;
if (config.get('enableDeveloperExperiments')) {
membersHelper += `<script defer src="https://unpkg.com/@tryghost/members-js@latest/umd/members.min.js" data-ghost="${urlUtils.getSiteUrl()}"></script>`;
}
if ((!!stripeSecretToken && !!stripePublicToken) || !!stripeConnectIntegration.account_id) {
if ((!!stripeDirectSecretKey && !!stripeDirectPublishableKey) || !!stripeConnectAccountId) {
membersHelper += '<script src="https://js.stripe.com/v3/"></script>';
}
return membersHelper;

View file

@ -61,12 +61,9 @@ function haxGetMembersPriceData() {
};
try {
const membersSettings = settingsCache.get('members_subscription_settings');
const stripeProcessor = membersSettings.paymentProcessors.find(
processor => processor.adapter === 'stripe'
);
const stripePlans = settingsCache.get('stripe_plans');
const priceData = stripeProcessor.config.plans.reduce((prices, plan) => {
const priceData = stripePlans.reduce((prices, plan) => {
const numberAmount = 0 + plan.amount;
const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
return Object.assign(prices, {
@ -74,7 +71,7 @@ function haxGetMembersPriceData() {
});
}, {});
priceData.currency = String.prototype.toUpperCase.call(stripeProcessor.config.currency || 'usd');
priceData.currency = stripePlans[0].currency;
priceData.currency_symbol = CURRENCY_SYMBOLS[priceData.currency];
if (Number.isInteger(priceData.monthly) && Number.isInteger(priceData.yearly)) {

View file

@ -92,12 +92,9 @@ module.exports = {
try {
const updatedFromAddress = membersService.settings.getEmailFromToken({token: frame.options.token});
if (updatedFromAddress) {
let subscriptionSetting = settingsCache.get('members_subscription_settings', {resolve: false});
const settingsValue = subscriptionSetting.value ? JSON.parse(subscriptionSetting.value) : {};
settingsValue.fromAddress = updatedFromAddress;
return models.Settings.edit({
key: 'members_subscription_settings',
value: JSON.stringify(settingsValue)
key: 'members_from_address',
value: updatedFromAddress
}).then(() => {
// Redirect to Ghost-Admin settings page
const adminLink = membersService.settings.getAdminRedirectLink();
@ -155,10 +152,22 @@ module.exports = {
});
}
return models.Settings.edit({
key: 'stripe_connect_integration',
value: '{}'
}, frame.options);
return models.Settings.edit([{
key: 'stripe_connect_publishable_key',
value: null
}, {
key: 'stripe_connect_secret_key',
value: null
}, {
key: 'stripe_connect_livemode',
value: null
}, {
key: 'stripe_connect_display_name',
value: null
}, {
key: 'stripe_connect_account_id',
value: null
}], frame.options);
}
},
@ -187,9 +196,8 @@ module.exports = {
const stripeConnectIntegrationToken = frame.data.settings.find(setting => setting.key === 'stripe_connect_integration_token');
// The `stripe_connect_integration_token` "setting" is only used to set the `stripe_connect_integration` setting.
// The `stripe_connect_integration` setting is not allowed to be set directly.
const settings = frame.data.settings.filter((setting) => {
return !['stripe_connect_integration', 'stripe_connect_integration_token'].includes(setting.key);
return !['stripe_connect_integration_token'].includes(setting.key);
});
const getSetting = setting => settingsCache.get(setting.key, {resolve: false});
@ -218,8 +226,24 @@ module.exports = {
try {
const data = await membersService.stripeConnect.getStripeConnectTokenData(stripeConnectIntegrationToken.value, getSessionProp);
settings.push({
key: 'stripe_connect_integration',
value: JSON.stringify(data)
key: 'stripe_connect_publishable_key',
value: data.public_key
});
settings.push({
key: 'stripe_connect_secret_key',
value: data.secret_key
});
settings.push({
key: 'stripe_connect_livemode',
value: data.livemode
});
settings.push({
key: 'stripe_connect_display_name',
value: data.display_name
});
settings.push({
key: 'stripe_connect_account_id',
value: data.account_id
});
} catch (err) {
throw new BadRequestError({

View file

@ -1,7 +1,6 @@
const _ = require('lodash');
const url = require('./utils/url');
const typeGroupMapper = require('./utils/settings-type-group-mapper');
const settingsCache = require('../../../../../services/settings/cache');
module.exports = {
browse(apiConfig, frame) {
@ -85,17 +84,6 @@ module.exports = {
const {apiKey = '', domain = '', baseUrl = '', provider = 'mailgun'} = setting.value ? JSON.parse(setting.value) : {};
setting.value = JSON.stringify({apiKey, domain, baseUrl, provider});
}
//CASE: Ensure we don't update fromAddress for member as that goes through magic link flow
if (setting.key === 'members_subscription_settings') {
const memberSubscriptionSettings = setting.value ? JSON.parse(setting.value) : {};
let subscriptionSettingCache = settingsCache.get('members_subscription_settings', {resolve: false});
const settingsCacheValue = subscriptionSettingCache.value ? JSON.parse(subscriptionSettingCache.value) : {};
memberSubscriptionSettings.fromAddress = settingsCacheValue.fromAddress;
setting.value = JSON.stringify(memberSubscriptionSettings);
}
});
}
};

View file

@ -125,10 +125,8 @@ class SettingsImporter extends BaseImporter {
}
// CASE: we do not import "from address" for members settings as that needs to go via validation with magic link
if (obj.key === 'members_subscription_settings' && obj.value) {
const oldMemberSettings = _.find(this.existingData, {key: 'members_subscription_settings'}) || {};
const oldMemberSettingsVal = oldMemberSettings.value ? JSON.parse(oldMemberSettings.value) : {};
obj.value = JSON.stringify(_.assign({}, JSON.parse(obj.value), {fromAddress: oldMemberSettingsVal.fromAddress}));
if (obj.key === 'members_from_address') {
obj.value = null;
}
// CASE: export files might contain "0" or "1" for booleans. Model layer needs real booleans.

View file

@ -183,11 +183,38 @@
"default_content_visibility": {
"defaultValue": "public"
},
"members_subscription_settings": {
"defaultValue": "{\"fromAddress\":\"noreply\",\"allowSelfSignup\":true,\"paymentProcessors\":[{\"adapter\":\"stripe\",\"config\":{\"secret_token\":\"\",\"public_token\":\"\",\"product\":{\"name\":\"Ghost Subscription\"},\"plans\":[{\"name\":\"Monthly\",\"currency\":\"usd\",\"interval\":\"month\",\"amount\":\"\"},{\"name\":\"Yearly\",\"currency\":\"usd\",\"interval\":\"year\",\"amount\":\"\"}]}}]}"
"members_allow_free_signup": {
"defaultValue": true
},
"stripe_connect_integration": {
"defaultValue": "{}"
"members_from_address": {
"defaultValue": null
},
"stripe_product_name": {
"defaultValue": "Ghost Subscription"
},
"stripe_secret_key": {
"defaultValue": null
},
"stripe_publishable_key": {
"defaultValue": null
},
"stripe_plans": {
"defaultValue": "[{\"name\":\"Monthly\",\"currency\":\"usd\",\"interval\":\"month\",\"amount\":5},{\"name\":\"Yearly\",\"currency\":\"usd\",\"interval\":\"year\",\"amount\":50}]"
},
"stripe_connect_publishable_key": {
"defaultValue": null
},
"stripe_connect_secret_key": {
"defaultValue": null
},
"stripe_connect_livemode": {
"defaultValue": null
},
"stripe_connect_display_name": {
"defaultValue": null
},
"stripe_connect_account_id": {
"defaultValue": null
}
},
"portal" : {

View file

@ -34,11 +34,8 @@ class MembersConfigProvider {
return domain && domain[1];
}
/**
*/
getEmailFromAddress() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings') || {};
const fromAddress = subscriptionSettings.fromAddress || 'noreply';
const fromAddress = this._settingsCache.get('members_from_address') || 'noreply';
// Any fromAddress without domain uses site domain, like default setting `noreply`
if (fromAddress.indexOf('@') < 0) {
@ -56,18 +53,18 @@ class MembersConfigProvider {
EUR: '€',
INR: '₹'
};
const defaultPriceData = {
monthly: 0,
yearly: 0
yearly: 0,
currency: 'USD',
currency_symbol: CURRENCY_SYMBOLS.USD
};
try {
const membersSettings = this._settingsCache.get('members_subscription_settings');
const stripeProcessor = membersSettings.paymentProcessors.find(
processor => processor.adapter === 'stripe'
);
const plans = this._settingsCache.get('stripe_plans');
const priceData = stripeProcessor.config.plans.reduce((prices, plan) => {
const priceData = plans.reduce((prices, plan) => {
const numberAmount = 0 + plan.amount;
const dollarAmount = numberAmount ? Math.round(numberAmount / 100) : 0;
return Object.assign(prices, {
@ -75,7 +72,7 @@ class MembersConfigProvider {
});
}, {});
priceData.currency = String.prototype.toUpperCase.call(stripeProcessor.config.currency || 'usd');
priceData.currency = plans[0].currency || 'USD';
priceData.currency_symbol = CURRENCY_SYMBOLS[priceData.currency];
if (Number.isInteger(priceData.monthly) && Number.isInteger(priceData.yearly)) {
@ -89,52 +86,51 @@ class MembersConfigProvider {
}
/**
* @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}}
* @param {'direct' | 'connect'} type - The "type" of keys to fetch from settings
* @returns {{publicKey: string, secretKey: string} | null}
*/
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
};
getStripeKeys(type) {
if (type !== 'direct' && type !== 'connect') {
throw new Error();
}
return {
publicKey: stripeConnectIntegration.public_key,
secretKey: stripeConnectIntegration.secret_key
};
}
const secretKey = this._settingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}secret_key`);
const publicKey = this._settingsCache.get(`stripe_${type === 'connect' ? 'connect_' : ''}publishable_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) {
if (!secretKey || !publicKey) {
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);
return {
secretKey,
publicKey
};
}
/**
* @returns {{publicKey: string, secretKey: string} | null}
*/
getActiveStripeKeys() {
const stripeDirect = this._config.get('stripeDirect');
if (stripeDirect) {
return this.getStripeKeys('direct');
}
const connectKeys = this.getStripeKeys('connect');
if (!connectKeys) {
return this.getStripeKeys('direct');
}
return connectKeys;
}
isStripeConnected() {
return this.getActiveStripeKeys() !== null;
}
getStripeUrlConfig() {
const siteUrl = this._urlUtils.getSiteUrl();
const webhookHandlerUrl = new URL('/members/webhooks/stripe', siteUrl);
@ -149,25 +145,39 @@ class MembersConfigProvider {
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 {
checkoutSuccess: checkoutSuccessUrl.href,
checkoutCancel: checkoutCancelUrl.href,
billingSuccess: billingSuccessUrl.href,
billingCancel: billingCancelUrl.href,
webhookHandler: webhookHandlerUrl.href
};
}
if (!stripeApiKeys.publicKey || !stripeApiKeys.secretKey) {
getStripePaymentConfig() {
if (!this.isStripeConnected()) {
return null;
}
const stripeApiKeys = this.getActiveStripeKeys();
const urls = this.getStripeUrlConfig();
if (!stripeApiKeys) {
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,
checkoutSuccessUrl: urls.checkoutSuccess,
checkoutCancelUrl: urls.checkoutCancel,
billingSuccessUrl: urls.billingSuccess,
billingCancelUrl: urls.billingCancel,
webhookHandlerUrl: urls.webhookHandler,
product: {
name: this._settingsCache.get('stripe_product_name')
},
plans: [COMPLIMENTARY_PLAN].concat(this._settingsCache.get('stripe_plans')),
appInfo: {
name: 'Ghost',
partner_id: 'pp_partner_DKmRVtTs4j9pwZ',
@ -192,8 +202,7 @@ class MembersConfigProvider {
}
getAllowSelfSignup() {
const subscriptionSettings = this._settingsCache.get('members_subscription_settings');
return subscriptionSettings.allowSelfSignup;
return this._settingsCache.get('members_allow_free_signup');
}
getTokenConfig() {

View file

@ -23,7 +23,19 @@ let membersSettings;
// Bind to events to automatically keep subscription info up-to-date from settings
events.on('settings.edited', function updateSettingFromModel(settingModel) {
if (!['members_subscription_settings', 'stripe_connect_integration'].includes(settingModel.get('key'))) {
if (![
'members_allow_free_signup',
'members_from_address',
'stripe_publishable_key',
'stripe_secret_key',
'stripe_product_name',
'stripe_plans',
'stripe_connect_publishable_key',
'stripe_connect_secret_key',
'stripe_connect_livemode',
'stripe_connect_display_name',
'stripe_connect_account_id'
].includes(settingModel.get('key'))) {
return;
}

View file

@ -84,14 +84,8 @@ const updateMemberData = async function (req, res) {
};
const getMemberSiteData = async function (req, res) {
const stripePaymentProcessor = settingsCache.get('members_subscription_settings').paymentProcessors.find(
paymentProcessor => paymentProcessor.adapter === 'stripe'
);
const stripeSecretToken = stripePaymentProcessor && stripePaymentProcessor.config.secret_token;
const stripePublicToken = stripePaymentProcessor && stripePaymentProcessor.config.public_token;
const stripeConnectIntegration = settingsCache.get('stripe_connect_integration');
const isStripeConfigured = membersService.config.isStripeConnected();
const isStripeConfigured = (!!stripeSecretToken && !!stripePublicToken) || !!(stripeConnectIntegration && stripeConnectIntegration.account_id);
const response = {
title: settingsCache.get('title'),
description: settingsCache.get('description'),

View file

@ -9,45 +9,54 @@ const ghost = testUtils.startGhost;
// NOTE: in future iterations these fields should be fetched from a central module.
// Have put a list as is here for the lack of better place for it.
const defaultSettingsKeyTypes = [
{key: 'title',type: 'blog'},
{key: 'description',type: 'blog'},
{key: 'logo',type: 'blog'},
{key: 'cover_image',type: 'blog'},
{key: 'icon',type: 'blog'},
{key: 'lang',type: 'blog'},
{key: 'timezone',type: 'blog'},
{key: 'codeinjection_head',type: 'blog'},
{key: 'codeinjection_foot',type: 'blog'},
{key: 'facebook',type: 'blog'},
{key: 'twitter',type: 'blog'},
{key: 'navigation',type: 'blog'},
{key: 'secondary_navigation',type: 'blog'},
{key: 'meta_title',type: 'blog'},
{key: 'meta_description',type: 'blog'},
{key: 'og_image',type: 'blog'},
{key: 'og_title',type: 'blog'},
{key: 'og_description',type: 'blog'},
{key: 'twitter_image',type: 'blog'},
{key: 'twitter_title',type: 'blog'},
{key: 'twitter_description',type: 'blog'},
{key: 'active_theme',type: 'theme'},
{key: 'is_private',type: 'private'},
{key: 'password',type: 'private'},
{key: 'public_hash',type: 'private'},
{key: 'default_content_visibility',type: 'members'},
{key: 'members_subscription_settings',type: 'members'},
{key: 'stripe_connect_integration',type: 'members'},
{key: 'portal_name',type: 'portal'},
{key: 'portal_button',type: 'portal'},
{key: 'portal_plans',type: 'portal'},
{key: 'bulk_email_settings',type: 'bulk_email'},
{key: 'amp',type: 'blog'},
{key: 'labs',type: 'blog'},
{key: 'slack',type: 'blog'},
{key: 'unsplash',type: 'blog'},
{key: 'shared_views',type: 'blog'},
{key: 'active_timezone',type: 'blog'},
{key: 'default_locale',type: 'blog'}
{key: 'title', type: 'blog'},
{key: 'description', type: 'blog'},
{key: 'logo', type: 'blog'},
{key: 'cover_image', type: 'blog'},
{key: 'icon', type: 'blog'},
{key: 'lang', type: 'blog'},
{key: 'timezone', type: 'blog'},
{key: 'codeinjection_head', type: 'blog'},
{key: 'codeinjection_foot', type: 'blog'},
{key: 'facebook', type: 'blog'},
{key: 'twitter', type: 'blog'},
{key: 'navigation', type: 'blog'},
{key: 'secondary_navigation', type: 'blog'},
{key: 'meta_title', type: 'blog'},
{key: 'meta_description', type: 'blog'},
{key: 'og_image', type: 'blog'},
{key: 'og_title', type: 'blog'},
{key: 'og_description', type: 'blog'},
{key: 'twitter_image', type: 'blog'},
{key: 'twitter_title', type: 'blog'},
{key: 'twitter_description', type: 'blog'},
{key: 'active_theme', type: 'theme'},
{key: 'is_private', type: 'private'},
{key: 'password', type: 'private'},
{key: 'public_hash', type: 'private'},
{key: 'default_content_visibility', type: 'members'},
{key: 'members_allow_free_signup', type: 'members'},
{key: 'members_from_address', type: 'members'},
{key: 'stripe_product_name', type: 'members'},
{key: 'stripe_plans', type: 'members'},
{key: 'stripe_secret_key', type: 'members'},
{key: 'stripe_publishable_key', type: 'members'},
{key: 'stripe_connect_secret_key', type: 'members'},
{key: 'stripe_connect_publishable_key', type: 'members'},
{key: 'stripe_connect_account_id', type: 'members'},
{key: 'stripe_connect_display_name', type: 'members'},
{key: 'stripe_connect_livemode', type: 'members'},
{key: 'portal_name', type: 'portal'},
{key: 'portal_button', type: 'portal'},
{key: 'portal_plans', type: 'portal'},
{key: 'bulk_email_settings', type: 'bulk_email'},
{key: 'amp', type: 'blog'},
{key: 'labs', type: 'blog'},
{key: 'slack', type: 'blog'},
{key: 'unsplash', type: 'blog'},
{key: 'shared_views', type: 'blog'},
{key: 'active_timezone', type: 'blog'},
{key: 'default_locale', type: 'blog'}
];
describe('Settings API (canary)', function () {
@ -82,8 +91,12 @@ describe('Settings API (canary)', function () {
jsonResponse.settings.should.be.an.Object();
const settings = jsonResponse.settings;
Object.keys(settings).length.should.equal(39);
settings.map(s => ({key: s.key, type: s.type})).should.deepEqual(defaultSettingsKeyTypes);
should.equal(settings.length, defaultSettingsKeyTypes.length);
for (const defaultSetting of defaultSettingsKeyTypes) {
should.exist(settings.find((setting) => {
return setting.key === defaultSetting.key && setting.type === defaultSetting.type;
}));
}
localUtils.API.checkResponse(jsonResponse, 'settings');
});

View file

@ -8,45 +8,54 @@ const ghost = testUtils.startGhost;
// NOTE: in future iterations these fields should be fetched from a central module.
// Have put a list as is here for the lack of better place for it.
const defaultSettingsKeyTypes = [
{key: 'title',type: 'blog'},
{key: 'description',type: 'blog'},
{key: 'logo',type: 'blog'},
{key: 'cover_image',type: 'blog'},
{key: 'icon',type: 'blog'},
{key: 'codeinjection_head',type: 'blog'},
{key: 'codeinjection_foot',type: 'blog'},
{key: 'facebook',type: 'blog'},
{key: 'twitter',type: 'blog'},
{key: 'navigation',type: 'blog'},
{key: 'secondary_navigation',type: 'blog'},
{key: 'meta_title',type: 'blog'},
{key: 'meta_description',type: 'blog'},
{key: 'og_image',type: 'blog'},
{key: 'og_title',type: 'blog'},
{key: 'og_description',type: 'blog'},
{key: 'twitter_image',type: 'blog'},
{key: 'twitter_title',type: 'blog'},
{key: 'twitter_description',type: 'blog'},
{key: 'active_theme',type: 'theme'},
{key: 'is_private',type: 'private'},
{key: 'password',type: 'private'},
{key: 'public_hash',type: 'private'},
{key: 'default_content_visibility',type: 'members'},
{key: 'members_subscription_settings',type: 'members'},
{key: 'stripe_connect_integration',type: 'members'},
{key: 'portal_name',type: 'portal'},
{key: 'portal_button',type: 'portal'},
{key: 'portal_plans',type: 'portal'},
{key: 'bulk_email_settings',type: 'bulk_email'},
{key: 'amp',type: 'blog'},
{key: 'labs',type: 'blog'},
{key: 'slack',type: 'blog'},
{key: 'unsplash',type: 'blog'},
{key: 'shared_views',type: 'blog'},
{key: 'ghost_head',type: 'blog'},
{key: 'ghost_foot',type: 'blog'},
{key: 'active_timezone',type: 'blog'},
{key: 'default_locale',type: 'blog'}
{key: 'title', type: 'blog'},
{key: 'description', type: 'blog'},
{key: 'logo', type: 'blog'},
{key: 'cover_image', type: 'blog'},
{key: 'icon', type: 'blog'},
{key: 'codeinjection_head', type: 'blog'},
{key: 'codeinjection_foot', type: 'blog'},
{key: 'facebook', type: 'blog'},
{key: 'twitter', type: 'blog'},
{key: 'navigation', type: 'blog'},
{key: 'secondary_navigation', type: 'blog'},
{key: 'meta_title', type: 'blog'},
{key: 'meta_description', type: 'blog'},
{key: 'og_image', type: 'blog'},
{key: 'og_title', type: 'blog'},
{key: 'og_description', type: 'blog'},
{key: 'twitter_image', type: 'blog'},
{key: 'twitter_title', type: 'blog'},
{key: 'twitter_description', type: 'blog'},
{key: 'active_theme', type: 'theme'},
{key: 'is_private', type: 'private'},
{key: 'password', type: 'private'},
{key: 'public_hash', type: 'private'},
{key: 'default_content_visibility', type: 'members'},
{key: 'members_allow_free_signup', type: 'members'},
{key: 'members_from_address', type: 'members'},
{key: 'stripe_product_name', type: 'members'},
{key: 'stripe_plans', type: 'members'},
{key: 'stripe_secret_key', type: 'members'},
{key: 'stripe_publishable_key', type: 'members'},
{key: 'stripe_connect_secret_key', type: 'members'},
{key: 'stripe_connect_publishable_key', type: 'members'},
{key: 'stripe_connect_account_id', type: 'members'},
{key: 'stripe_connect_display_name', type: 'members'},
{key: 'stripe_connect_livemode', type: 'members'},
{key: 'portal_name', type: 'portal'},
{key: 'portal_button', type: 'portal'},
{key: 'portal_plans', type: 'portal'},
{key: 'bulk_email_settings', type: 'bulk_email'},
{key: 'amp', type: 'blog'},
{key: 'labs', type: 'blog'},
{key: 'slack', type: 'blog'},
{key: 'unsplash', type: 'blog'},
{key: 'shared_views', type: 'blog'},
{key: 'ghost_head', type: 'blog'},
{key: 'ghost_foot', type: 'blog'},
{key: 'active_timezone', type: 'blog'},
{key: 'default_locale', type: 'blog'}
];
describe('Settings API (v2)', function () {
@ -81,8 +90,12 @@ describe('Settings API (v2)', function () {
jsonResponse.settings.should.be.an.Object();
const settings = jsonResponse.settings;
Object.keys(settings).length.should.equal(39);
settings.map(s => ({key: s.key, type: s.type})).should.deepEqual(defaultSettingsKeyTypes);
should.equal(settings.length, defaultSettingsKeyTypes.length);
for (const defaultSetting of defaultSettingsKeyTypes) {
should.exist(settings.find((setting) => {
return setting.key === defaultSetting.key && setting.type === defaultSetting.type;
}));
}
localUtils.API.checkResponse(jsonResponse, 'settings');
});

View file

@ -35,8 +35,17 @@ const defaultSettingsKeys = [
'password',
'public_hash',
'default_content_visibility',
'members_subscription_settings',
'stripe_connect_integration',
'members_allow_free_signup',
'members_from_address',
'stripe_product_name',
'stripe_plans',
'stripe_secret_key',
'stripe_publishable_key',
'stripe_connect_secret_key',
'stripe_connect_publishable_key',
'stripe_connect_account_id',
'stripe_connect_display_name',
'stripe_connect_livemode',
'portal_name',
'portal_button',
'portal_plans',
@ -82,8 +91,7 @@ describe('Settings API (v3)', function () {
jsonResponse.settings.should.be.an.Object();
const settings = jsonResponse.settings;
Object.keys(settings).length.should.equal(39);
settings.map(s => s.key).should.deepEqual(defaultSettingsKeys);
settings.map(s => s.key).sort().should.deepEqual(defaultSettingsKeys.sort());
localUtils.API.checkResponse(jsonResponse, 'settings');
});

View file

@ -42,19 +42,17 @@ describe('SettingsImporter', function () {
it('Does not overwrite members from address', function () {
const fakeSettings = [{
key: 'members_subscription_settings',
value: '{"fromAddress":"newemail@example.com"}'
key: 'members_from_address',
value: 'newemail@example.com'
}];
const importer = new SettingsImporter({settings: fakeSettings}, {dataKeyToImport: 'settings'});
importer.beforeImport();
const membersetting = find(importer.dataToImport, {key: 'members_subscription_settings'});
const membersSettingsVal = JSON.parse(membersetting.value);
const membersFromAddress = membersSettingsVal.fromAddress;
const membersFromAddress = find(importer.dataToImport, {key: 'members_from_address'});
should.not.exist(membersFromAddress);
should.not.exist(membersFromAddress.value);
});
it('Adds a problem if the existing data is_private is false, and new data is_private is true', function () {

View file

@ -21,42 +21,31 @@ function createConfigMock({stripeDirectValue}) {
*/
function createSettingsMock({setDirect, setConnect}) {
const membersSubscriptionSettings = {
fromAddress: 'noreply',
allowSelfSignup: true,
paymentProcessors: [{
adapter: 'stripe',
config: {
secret_token: setDirect ? 'direct_secret' : null,
public_token: setDirect ? 'direct_public' : null,
product: {
name: 'Test'
},
plans: [{
name: 'Monthly',
currency: 'usd',
interval: 'month',
amount: 1000
}, {
name: 'Yearly',
currency: 'usd',
interval: 'year',
amount: 10000
}]
}
}]
};
const stripeConnectIntegration = {
secret_key: setConnect ? 'connect_secret' : null,
public_key: setConnect ? 'connect_public' : null,
livemode: true
};
const getStub = sinon.stub();
getStub.withArgs('members_subscription_settings').returns(membersSubscriptionSettings);
getStub.withArgs('stripe_connect_integration').returns(stripeConnectIntegration);
getStub.withArgs('members_from_address').returns('noreply');
getStub.withArgs('members_allow_free_signup').returns(true);
getStub.withArgs('stripe_secret_key').returns(setDirect ? 'direct_secret' : null);
getStub.withArgs('stripe_publishable_key').returns(setDirect ? 'direct_publishable' : null);
getStub.withArgs('stripe_product_name').returns('Test');
getStub.withArgs('stripe_plans').returns([{
name: 'Monthly',
currency: 'usd',
interval: 'month',
amount: 1000
}, {
name: 'Yearly',
currency: 'usd',
interval: 'year',
amount: 10000
}]);
getStub.withArgs('stripe_connect_secret_key').returns(setConnect ? 'connect_secret' : null);
getStub.withArgs('stripe_connect_publishable_key').returns(setConnect ? 'connect_publishable' : null);
getStub.withArgs('stripe_connect_livemode').returns(true);
getStub.withArgs('stripe_connect_display_name').returns('Test');
getStub.withArgs('stripe_connect_account_id').returns('ac_XXXXXXXXXXXXX');
return {
get: getStub
};
@ -77,7 +66,7 @@ describe('Members - config', function () {
const paymentConfig = membersConfig.getStripePaymentConfig();
should.equal(paymentConfig.publicKey, 'direct_public');
should.equal(paymentConfig.publicKey, 'direct_publishable');
should.equal(paymentConfig.secretKey, 'direct_secret');
});
@ -112,7 +101,7 @@ describe('Members - config', function () {
const paymentConfig = membersConfig.getStripePaymentConfig();
should.equal(paymentConfig.publicKey, 'connect_public');
should.equal(paymentConfig.publicKey, 'connect_publishable');
should.equal(paymentConfig.secretKey, 'connect_secret');
});
@ -130,7 +119,7 @@ describe('Members - config', function () {
const paymentConfig = membersConfig.getStripePaymentConfig();
should.equal(paymentConfig.publicKey, 'direct_public');
should.equal(paymentConfig.publicKey, 'direct_publishable');
should.equal(paymentConfig.secretKey, 'direct_secret');
});
});