diff --git a/api/languages/fr.php b/api/languages/fr.php index c6861a16..6a75ad28 100644 --- a/api/languages/fr.php +++ b/api/languages/fr.php @@ -55,3 +55,4 @@ . '$projectName', 'dateFormat' => 'd/m/Y' ]; + diff --git a/frontend/src/components/settings/Settings.jsx b/frontend/src/components/settings/Settings.jsx index 46ccfb9a..47c92e74 100644 --- a/frontend/src/components/settings/Settings.jsx +++ b/frontend/src/components/settings/Settings.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'; import { Box, Button, ButtonGroup, CircularProgress, Grid, InputLabel, LinearProgress, makeStyles, MenuItem, Paper, Select, TableContainer, Typography } from '@material-ui/core'; import { grey } from '@material-ui/core/colors'; import { useTranslation } from 'react-i18next'; -import { createBillingPortalSession, getAPIKeys, getMFADevices, getUserProfile, updateUserProfile } from '../../utils/API'; +import { createBillingPortalSession, getAPIKeys, getMFADevices, getSubscriptionLink, getUserProfile, updateUserProfile } from '../../utils/API'; import useTimezones from '../../hooks/useTimezones'; import useUserProfile from '../../hooks/useUserProfile'; import Breadcrumbs from '../misc/Breadcrumbs'; @@ -24,6 +24,7 @@ import DeleteMFADeviceDialog from './DeleteMFADeviceDialog'; import { RegexPatterns, SubscriptionStatus } from '../../utils/Constants'; import DeleteAccountDialog from './DeleteAccountDialog'; import ManageSubscriptionIcon from '@material-ui/icons/CreditCard'; +import CancelSubscriptionIcon from '@material-ui/icons/Cancel'; import SubscriptionActiveIcon from '@material-ui/icons/FavoriteBorder'; import SubscriptionInactiveIcon from '@material-ui/icons/PauseCircleOutline'; import LearnMoreIcon from '@material-ui/icons/Loyalty'; @@ -69,7 +70,7 @@ const useStyles = makeStyles(theme => ({ } })); -const REFRESH_INTERVAL = 5000; +const REFRESH_INTERVAL = 2000; export default function Settings() { const classes = useStyles(); @@ -82,6 +83,7 @@ export default function Settings() { const [ isLoading, setIsLoading ] = useState(true); const [ saving, setSaving ] = useState(false); const [ isLoadingManageSubscription, setIsLoadingManageSubscription ] = useState(false); + const [ isLoadingCancelSubscription, setIsLoadingCancelSubscription ] = useState(false); const [ showChangePassword, setShowChangePassword ] = useState(false); const [ showChangeEmail, setShowChangeEmail ] = useState(false); @@ -163,15 +165,37 @@ export default function Settings() { function manageSubscription() { setIsLoadingManageSubscription(true); - createBillingPortalSession() - .then(respone => window.location.href = respone.url) + + if (userProfile.userSubscription.type === 'stripe') { + createBillingPortalSession() + .then(respone => window.location.href = respone.url) + .catch(() => { + enqueueSnackbar(t('settings.manageSubscriptionFailed'), { variant: 'error' }); + setIsLoadingManageSubscription(false); + }); + } else if (userProfile.userSubscription.type === 'paddle') { + getSubscriptionLink('manage') + .then(response => window.location.href = response.url) + .catch(() => { + enqueueSnackbar(t('settings.manageSubscriptionFailed'), { variant: 'error' }); + setIsLoadingManageSubscription(false); + }); + } + } + + function cancelSubscription() { + setIsLoadingCancelSubscription(true); + + getSubscriptionLink('cancel') + .then(response => window.location.href = response.url) .catch(() => { enqueueSnackbar(t('settings.manageSubscriptionFailed'), { variant: 'error' }); - setIsLoadingManageSubscription(false); + setIsLoadingCancelSubscription(false); }); } const isCancelledSubscription = userProfile && userProfile.userSubscription && userProfile.userSubscription.status === SubscriptionStatus.CANCELLED; + const isExpiringSubscription = userProfile && userProfile.userSubscription && userProfile.userSubscription.status === SubscriptionStatus.EXPIRING; const isPaymentReturn = window && window.location && window.location.search === '?checkoutSuccess=true'; useEffect(() => { @@ -364,7 +388,7 @@ export default function Settings() { - {userProfile.userSubscription && + {userProfile.userSubscription && userProfile.userSubscription.type==='stripe' && } + {userProfile.userSubscription && userProfile.userSubscription.type==='paddle' && userProfile.userSubscription.status === SubscriptionStatus.ACTIVE && + + + } {!isPaymentReturn && ((!userProfile.userSubscription) || (userProfile.userSubscription.status === SubscriptionStatus.CANCELLED)) && diff --git a/frontend/src/locales/de/translation.json b/frontend/src/locales/de/translation.json index 14a040fc..f9d609a9 100644 --- a/frontend/src/locales/de/translation.json +++ b/frontend/src/locales/de/translation.json @@ -1,4 +1,6 @@ { + "paddleLocale": "de", + "landingLocale": "de", "common": { "menu": "Menü", "contact": "Kontakt", @@ -430,6 +432,8 @@ "manageSubscriptionFailed": "Fehler beim Laden des Abo-Verwaltungs-Portals. Bitte versuchen Sie es später erneut oder kontaktieren Sie unser Support-Team.", "orderFailed": "Fehler beim Laden des Kaufabwicklungs-Portals. Bitte versuchen Sie es später erneut oder kontaktieren Sie unser Support-Team.", "manageSubscription": "Abo verwalten", + "updatePaymentMethod": "Zahlungsart ändern", + "cancelSubscription": "Abo kündigen", "subscribeDialog": { "text1": "Durch eine Fördermitgliedschaft bei {{serviceName}} unterstützen Sie uns beim Betrieb und bei der Weiterentwicklung unseres Dienstes.", "text2": "Als Dankeschön für Ihre Unterstützung können wir Fördermitgliedern exklusive Vorteile anbieten.", @@ -450,7 +454,7 @@ "amountAMonth": "{{amount}} EUR / Monat", "amountAYear": "{{amount}} EUR / Jahr", "subscribeNow": "Jetzt Fördermitglied werden", - "orderNow": "Zahlungspflichtig bestellen", + "orderNow": "Jetzt bestellen", "orderText": "Vielen Dank für Ihre Unterstützung! Sie können nun Ihren Unterstützungsbeitrag wählen und die Buchung abschließen.", "acceptRefundPolicy": "Ich akzeptiere die Widerrufsbelehrung" }, diff --git a/frontend/src/locales/en/translation.json b/frontend/src/locales/en/translation.json index 023e7951..35b6d51a 100644 --- a/frontend/src/locales/en/translation.json +++ b/frontend/src/locales/en/translation.json @@ -1,4 +1,6 @@ { + "paddleLocale": "en", + "landingLocale": "en", "common": { "menu": "Menu", "contact": "Contact", @@ -454,6 +456,8 @@ }, "orderFailed": "Failed to load checkout portal. Please try again later or contact our support team.", "manageSubscription": "Manage subscription", + "updatePaymentMethod": "Update payment method", + "cancelSubscription": "Cancel subscription", "mfa": { "devices": "Multi-Factor Authentication devices", "noDevices": "No devices found. Add a MFA device now to secure your account.", diff --git a/frontend/src/locales/fr/translation.json b/frontend/src/locales/fr/translation.json index 6a2b0527..8254f90c 100644 --- a/frontend/src/locales/fr/translation.json +++ b/frontend/src/locales/fr/translation.json @@ -1,4 +1,5 @@ { + "paddleLocale": "fr", "common": { "menu": "Menu", "followontwitter": "Twitter", diff --git a/frontend/src/locales/it/translation.json b/frontend/src/locales/it/translation.json index 297be376..07ca9858 100644 --- a/frontend/src/locales/it/translation.json +++ b/frontend/src/locales/it/translation.json @@ -1,4 +1,5 @@ { + "paddleLocale": "it", "common": { "menu": "Menu", "contact": "Contatti", diff --git a/frontend/src/locales/ru/translation.json b/frontend/src/locales/ru/translation.json index fd898db4..71a72070 100644 --- a/frontend/src/locales/ru/translation.json +++ b/frontend/src/locales/ru/translation.json @@ -1,4 +1,5 @@ { + "paddleLocale": "de", "common": { "menu": "Меню", "followontwitter": "Twitter", diff --git a/frontend/src/utils/API.js b/frontend/src/utils/API.js index 3c8ee802..3331c66b 100644 --- a/frontend/src/utils/API.js +++ b/frontend/src/utils/API.js @@ -332,16 +332,14 @@ export function deleteJobTestRun(handle) { }); } -export function createCheckoutSession(product) { - return performRequest('CreateCheckoutSession', { - product - }); -} - export function createBillingPortalSession() { return performRequest('CreateBillingPortalSession', {}); } +export function getSubscriptionLink(type) { + return performRequest('GetSubscriptionLink', { type }); +} + export function getMFADevices() { return performRequest('GetMFADevices', {}); }