diff --git a/ghost/portal/src/App.js b/ghost/portal/src/App.js index 894dc78bcf..ac7eea8ade 100644 --- a/ghost/portal/src/App.js +++ b/ghost/portal/src/App.js @@ -9,7 +9,7 @@ import * as Fixtures from './utils/fixtures'; import ActionHandler from './actions'; import './App.css'; import NotificationParser from './utils/notifications'; -import {createPopupNotification, getCurrencySymbol, getFirstpromoterId, getSiteDomain, hasPrice, isComplimentaryMember, removePortalLinkFromUrl} from './utils/helpers'; +import {createPopupNotification, getAvailablePrices, getCurrencySymbol, getFirstpromoterId, getQueryPrice, getSiteDomain, isComplimentaryMember, removePortalLinkFromUrl} from './utils/helpers'; const React = require('react'); const DEV_MODE_DATA = { @@ -440,10 +440,12 @@ export default class App extends React.Component { /** Handle direct signup link for a price */ handleSignupQuery({site, pageQuery}) { - const queryPrice = hasPrice({site: site, plan: pageQuery}); + const queryPrice = getQueryPrice({site: site, priceId: pageQuery}); + const availablePrices = getAvailablePrices({site, includeFree: false}); + const isQueryPriceAvailable = availablePrices.some(d => d.id === pageQuery); if (!this.state.member - && ['monthly', 'yearly'].includes(pageQuery) && queryPrice + && isQueryPriceAvailable ) { removePortalLinkFromUrl(); this.dispatchAction('signup', {plan: queryPrice.id}); @@ -452,10 +454,17 @@ export default class App extends React.Component { /**Get Portal page from Link/Data-attribute path*/ getPageFromLinkPath(path) { + const customPricesSignupRegex = /^signup\/?(?:\/(\w+?))?\/?$/; if (path === 'signup') { return { page: 'signup' }; + } else if (customPricesSignupRegex.test(path)) { + const [, pageQuery] = path.match(customPricesSignupRegex); + return { + page: 'signup', + pageQuery: pageQuery + }; } else if (path === 'signup/free') { return { page: 'signup', diff --git a/ghost/portal/src/utils/fixtures.js b/ghost/portal/src/utils/fixtures.js index ceb5ea17bc..2a823f9436 100644 --- a/ghost/portal/src/utils/fixtures.js +++ b/ghost/portal/src/utils/fixtures.js @@ -108,7 +108,7 @@ export const site = { description: 'The default product' }, prices: prices, - allow_self_signup: false, + allow_self_signup: true, members_signup_access: 'all', is_stripe_configured: true, portal_button: true, diff --git a/ghost/portal/src/utils/helpers.js b/ghost/portal/src/utils/helpers.js index 696a17e14e..09a192d993 100644 --- a/ghost/portal/src/utils/helpers.js +++ b/ghost/portal/src/utils/helpers.js @@ -101,10 +101,26 @@ export function hasPrice({site = {}, plan}) { return prices && prices.length > 0 && prices.find(p => p.name === 'Monthly'); } else if (plan === 'yearly') { return prices && prices.length > 0 && prices.find(p => p.name === 'Yearly'); + } else if (plan) { + return prices && prices.length > 0 && prices.find(p => p.id === plan); } return false; } +export function getQueryPrice({site = {}, priceId}) { + const prices = getAvailablePrices({site}); + if (priceId === 'free') { + return !prices || prices.length === 0 || prices.find(p => p.type === 'free'); + } else if (priceId === 'monthly') { + return prices && prices.length > 0 && prices.find(p => p.name === 'Monthly'); + } else if (priceId === 'yearly') { + return prices && prices.length > 0 && prices.find(p => p.name === 'Yearly'); + } else if (priceId) { + return prices && prices.length > 0 && prices.find(p => p.id === priceId); + } + return null; +} + export function getProductDetails({site}) { if (site && site.product) { return { @@ -130,6 +146,49 @@ export function isInviteOnlySite({site = {}, pageQuery}) { return prices.length === 0 || (site && site.members_signup_access === 'invite'); } +export function getAvailablePrices({site = {}, includeFree = true} = {}) { + const { + prices, + allow_self_signup: allowSelfSignup, + is_stripe_configured: isStripeConfigured + } = site || {}; + + if (!prices) { + return []; + } + + const plansData = []; + + const stripePrices = prices.map((d) => { + return { + ...d, + price_id: d.id, + price: d.amount / 100, + name: d.nickname, + currency_symbol: getCurrencySymbol(d.currency) + }; + }).filter((price) => { + return price.amount !== 0 && price.type === 'recurring'; + }); + + if (allowSelfSignup && includeFree) { + plansData.push({ + id: 'free', + type: 'free', + price: 0, + currency_symbol: '$', + name: 'Free' + }); + } + + if (isStripeConfigured) { + stripePrices.forEach((price) => { + plansData.push(price); + }); + } + return plansData; +} + export function getSitePrices({site = {}, includeFree = true, pageQuery} = {}) { const { prices,