mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-20 17:32:15 +03:00
d16a8781a9
closes https://github.com/TryGhost/Team/issues/1319 Due to how JS implements numbers, it's possible that when we multiple a number with 2 decimal places by 100 that we do not end up with an integer e.g. 9.95 * 100 = 994.999... This is not a valid price for the API and so we must round it to the nearest integer. We round off prices both at source as well as in ties serializer to make sure we never send non integer prices to API.
214 lines
6.7 KiB
JavaScript
214 lines
6.7 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import envConfig from 'ghost-admin/config/environment';
|
|
import {action} from '@ember/object';
|
|
import {currencies, getCurrencyOptions, getSymbol} from 'ghost-admin/utils/currency';
|
|
import {inject as service} from '@ember/service';
|
|
import {task} from 'ember-concurrency';
|
|
import {tracked} from '@glimmer/tracking';
|
|
|
|
const CURRENCIES = currencies.map((currency) => {
|
|
return {
|
|
value: currency.isoCode.toLowerCase(),
|
|
label: `${currency.isoCode} - ${currency.name}`,
|
|
isoCode: currency.isoCode
|
|
};
|
|
});
|
|
|
|
export default class GhLaunchWizardSetPricingComponent extends Component {
|
|
@service config;
|
|
@service membersUtils;
|
|
@service settings;
|
|
@service store;
|
|
|
|
currencies = CURRENCIES;
|
|
|
|
@tracked stripeMonthlyAmount = 5;
|
|
@tracked stripeYearlyAmount = 50;
|
|
@tracked currency = 'usd';
|
|
@tracked isFreeChecked = true;
|
|
@tracked isMonthlyChecked = true;
|
|
@tracked isYearlyChecked = true;
|
|
@tracked stripePlanError = '';
|
|
@tracked product;
|
|
@tracked loadingProduct = false;
|
|
|
|
get selectedCurrency() {
|
|
return this.currencies.findBy('value', this.currency);
|
|
}
|
|
|
|
get allCurrencies() {
|
|
return getCurrencyOptions();
|
|
}
|
|
|
|
get isConnectDisallowed() {
|
|
const siteUrl = this.config.get('blogUrl');
|
|
|
|
return envConfig.environment !== 'development' && !/^https:/.test(siteUrl);
|
|
}
|
|
|
|
get isPaidPriceDisabled() {
|
|
return !this.membersUtils.isStripeEnabled;
|
|
}
|
|
|
|
get isFreeDisabled() {
|
|
return this.settings.get('membersSignupAccess') !== 'all';
|
|
}
|
|
|
|
willDestroy() {
|
|
super.willDestroy?.(...arguments);
|
|
// clear any unsaved settings changes when going back/forward/closing
|
|
this.args.updatePreview('');
|
|
}
|
|
|
|
@action
|
|
setup() {
|
|
this.fetchDefaultProduct.perform();
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
@action
|
|
backStep() {
|
|
const product = this.product;
|
|
const data = this.args.getData() || {};
|
|
this.args.storeData({
|
|
...data,
|
|
product,
|
|
isFreeChecked: this.isFreeChecked,
|
|
isMonthlyChecked: this.isMonthlyChecked,
|
|
isYearlyChecked: this.isYearlyChecked,
|
|
monthlyAmount: this.stripeMonthlyAmount,
|
|
yearlyAmount: this.stripeYearlyAmount,
|
|
currency: this.currency
|
|
});
|
|
this.args.backStep();
|
|
}
|
|
|
|
@action
|
|
setStripePlansCurrency(event) {
|
|
const newCurrency = event.value;
|
|
this.currency = newCurrency;
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
@action
|
|
toggleFreePlan(event) {
|
|
this.isFreeChecked = event.target.checked;
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
@action
|
|
toggleMonthlyPlan(event) {
|
|
this.isMonthlyChecked = event.target.checked;
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
@action
|
|
toggleYearlyPlan(event) {
|
|
this.isYearlyChecked = event.target.checked;
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
@action
|
|
validateStripePlans() {
|
|
this.stripePlanError = undefined;
|
|
|
|
try {
|
|
const yearlyAmount = this.stripeYearlyAmount;
|
|
const monthlyAmount = this.stripeMonthlyAmount;
|
|
const symbol = getSymbol(this.currency);
|
|
if (!yearlyAmount || yearlyAmount < 1 || !monthlyAmount || monthlyAmount < 1) {
|
|
throw new TypeError(`Subscription amount must be at least ${symbol}1.00`);
|
|
}
|
|
|
|
this.updatePreviewUrl();
|
|
} catch (err) {
|
|
this.stripePlanError = err.message;
|
|
}
|
|
}
|
|
|
|
@task
|
|
*saveAndContinue() {
|
|
if (this.isConnectDisallowed) {
|
|
this.args.nextStep();
|
|
} else {
|
|
yield this.validateStripePlans();
|
|
|
|
if (this.stripePlanError) {
|
|
return false;
|
|
}
|
|
const product = this.product;
|
|
const data = this.args.getData() || {};
|
|
this.args.storeData({
|
|
...data,
|
|
product,
|
|
isFreeChecked: this.isFreeChecked,
|
|
isMonthlyChecked: this.isMonthlyChecked,
|
|
isYearlyChecked: this.isYearlyChecked,
|
|
monthlyAmount: this.stripeMonthlyAmount,
|
|
yearlyAmount: this.stripeYearlyAmount,
|
|
currency: this.currency
|
|
});
|
|
this.args.nextStep();
|
|
}
|
|
}
|
|
|
|
@task({drop: true})
|
|
*fetchDefaultProduct() {
|
|
const storedData = this.args.getData();
|
|
if (storedData?.product) {
|
|
this.product = storedData.product;
|
|
|
|
if (storedData.isMonthlyChecked !== undefined) {
|
|
this.isMonthlyChecked = storedData.isMonthlyChecked;
|
|
}
|
|
if (storedData.isYearlyChecked !== undefined) {
|
|
this.isYearlyChecked = storedData.isYearlyChecked;
|
|
}
|
|
if (storedData.isFreeChecked !== undefined) {
|
|
this.isFreeChecked = storedData.isFreeChecked;
|
|
}
|
|
if (storedData.currency !== undefined) {
|
|
this.currency = storedData.currency;
|
|
}
|
|
this.stripeMonthlyAmount = storedData.monthlyAmount;
|
|
this.stripeYearlyAmount = storedData.yearlyAmount;
|
|
} else {
|
|
const products = yield this.store.query('product', {filter: 'type:paid', include: 'monthly_price,yearly_price'});
|
|
this.product = products.firstObject;
|
|
|
|
let portalPlans = this.settings.get('portalPlans') || [];
|
|
|
|
this.isMonthlyChecked = portalPlans.includes('monthly');
|
|
this.isYearlyChecked = portalPlans.includes('yearly');
|
|
this.isFreeChecked = portalPlans.includes('free');
|
|
|
|
const monthlyPrice = this.product.get('monthlyPrice');
|
|
const yearlyPrice = this.product.get('yearlyPrice');
|
|
if (monthlyPrice && monthlyPrice.amount) {
|
|
this.stripeMonthlyAmount = (monthlyPrice.amount / 100);
|
|
this.currency = monthlyPrice.currency;
|
|
}
|
|
if (yearlyPrice && yearlyPrice.amount) {
|
|
this.stripeYearlyAmount = (yearlyPrice.amount / 100);
|
|
}
|
|
}
|
|
this.updatePreviewUrl();
|
|
}
|
|
|
|
updatePreviewUrl() {
|
|
const options = {
|
|
disableBackground: true,
|
|
currency: this.selectedCurrency.value,
|
|
monthlyPrice: Math.round(this.stripeMonthlyAmount * 100),
|
|
yearlyPrice: Math.round(this.stripeYearlyAmount * 100),
|
|
isMonthlyChecked: this.isMonthlyChecked,
|
|
isYearlyChecked: this.isYearlyChecked,
|
|
isFreeChecked: this.isFreeChecked,
|
|
portalPlans: null
|
|
};
|
|
|
|
const url = this.membersUtils.getPortalPreviewUrl(options);
|
|
this.args.updatePreview(url);
|
|
}
|
|
}
|