diff --git a/src/pages/Subscription.tsx b/src/pages/Subscription.tsx
index 402de39..e5b1891 100644
--- a/src/pages/Subscription.tsx
+++ b/src/pages/Subscription.tsx
@@ -312,6 +312,28 @@ export default function Subscription() {
queryClient.invalidateQueries({ queryKey: ['purchase-options'] });
setSwitchTariffId(null);
},
+ onError: (error: unknown) => {
+ // Handle subscription_expired error - redirect to purchase flow
+ if (error instanceof AxiosError) {
+ const detail = error.response?.data?.detail;
+ if (
+ typeof detail === 'object' &&
+ detail?.error_code === 'subscription_expired' &&
+ detail?.use_purchase_flow === true
+ ) {
+ // Find the tariff user was trying to switch to and open purchase form
+ const targetTariff = tariffs.find((t) => t.id === switchTariffId);
+ if (targetTariff) {
+ setSwitchTariffId(null);
+ setSelectedTariff(targetTariff);
+ setSelectedTariffPeriod(targetTariff.periods[0] || null);
+ setShowTariffPurchase(true);
+ // Refetch purchase-options to get updated expired status
+ queryClient.invalidateQueries({ queryKey: ['purchase-options'] });
+ }
+ }
+ }
+ },
});
// Tariff purchase mutation
@@ -1955,6 +1977,40 @@ export default function Subscription() {
)}
+ {/* Expired subscription notice - prompt to purchase new tariff */}
+ {isTariffsMode &&
+ purchaseOptions &&
+ 'subscription_is_expired' in purchaseOptions &&
+ purchaseOptions.subscription_is_expired && (
+
+
+
+
+
+ {t('subscription.expired.title')}
+
+
+ {t('subscription.expired.selectTariff')}
+
+
+
+
+ )}
+
{/* Legacy subscription notice - if user has subscription without tariff */}
{subscription && !subscription.is_trial && !subscription.tariff_id && (
@@ -2066,6 +2122,27 @@ export default function Subscription() {
t('subscription.switchTariff.switch')
)}
+
+ {/* Show error (except subscription_expired which redirects to purchase) */}
+ {switchTariffMutation.isError &&
+ (() => {
+ const detail =
+ switchTariffMutation.error instanceof AxiosError
+ ? switchTariffMutation.error.response?.data?.detail
+ : null;
+ // Skip displaying if it's subscription_expired (handled by redirect)
+ if (
+ typeof detail === 'object' &&
+ detail?.error_code === 'subscription_expired'
+ ) {
+ return null;
+ }
+ return (
+
+ {getErrorMessage(switchTariffMutation.error)}
+
+ );
+ })()}
>
);
})()
@@ -2125,11 +2202,20 @@ export default function Subscription() {
.map((tariff) => {
const isCurrentTariff =
tariff.is_current || tariff.id === subscription?.tariff_id;
+ // Check if subscription is expired from purchaseOptions
+ const isSubscriptionExpired =
+ isTariffsMode &&
+ purchaseOptions &&
+ 'subscription_is_expired' in purchaseOptions &&
+ purchaseOptions.subscription_is_expired === true;
+ // canSwitch only if subscription is active (not expired, not trial)
const canSwitch =
subscription &&
subscription.tariff_id &&
!isCurrentTariff &&
- !subscription.is_trial;
+ !subscription.is_trial &&
+ !isSubscriptionExpired &&
+ subscription.is_active;
// Если есть подписка БЕЗ tariff_id (классическая) - разрешить выбрать тариф
const isLegacySubscription =
subscription && !subscription.is_trial && !subscription.tariff_id;