mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
fix(switch-tariff): route trial & expired subs to the purchase flow (#629889)
The switch sheet's fallback checked detail.error_code, but the backend sends detail.code — so the expired-fallback was effectively dead. Centralize it into shouldUsePurchaseFlow(), which accepts either key and now also handles the new trial_cannot_switch code: a trial has no paid value to prorate, so the backend rejects the switch and the sheet hands the tariff to the regular purchase form instead of surfacing a raw error. The switch button is already hidden for trials via canSwitch; this is the server-side backstop for races / direct calls, and it un-breaks the expired fallback at the same time.
This commit is contained in:
@@ -26,6 +26,25 @@ import type { Tariff } from '../../../types';
|
||||
// TariffPurchaseForm instead of attempting another switch.
|
||||
// ──────────────────────────────────────────────────────────────────
|
||||
|
||||
// The backend rejects a switch that must instead go through the purchase flow:
|
||||
// the subscription lapsed (`subscription_expired`), or it is a trial that has no
|
||||
// paid value to prorate and would otherwise be handed a full target period
|
||||
// (`trial_cannot_switch`, bug #629889). Both arrive as detail.code +
|
||||
// use_purchase_flow=true; some payloads use the legacy `error_code` key, so we
|
||||
// accept either.
|
||||
function shouldUsePurchaseFlow(error: unknown): boolean {
|
||||
if (!(error instanceof AxiosError)) return false;
|
||||
const detail = error.response?.data?.detail as
|
||||
| { code?: string; error_code?: string; use_purchase_flow?: boolean }
|
||||
| undefined;
|
||||
if (!detail || typeof detail !== 'object') return false;
|
||||
const code = detail.code ?? detail.error_code;
|
||||
return (
|
||||
(code === 'subscription_expired' || code === 'trial_cannot_switch') &&
|
||||
detail.use_purchase_flow === true
|
||||
);
|
||||
}
|
||||
|
||||
export interface SwitchTariffSheetProps {
|
||||
open: boolean;
|
||||
tariffId: number | null;
|
||||
@@ -69,22 +88,15 @@ export function SwitchTariffSheet({
|
||||
navigate('/subscriptions', { replace: true });
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
// Backend signal: the subscription lapsed mid-flight. Hand the
|
||||
// selected tariff back to the parent so it can open the regular
|
||||
// purchase form instead.
|
||||
if (error instanceof AxiosError) {
|
||||
const detail = error.response?.data?.detail;
|
||||
if (
|
||||
typeof detail === 'object' &&
|
||||
detail?.error_code === 'subscription_expired' &&
|
||||
detail?.use_purchase_flow === true
|
||||
) {
|
||||
const targetTariff = tariffs.find((tariff) => tariff.id === tariffId);
|
||||
if (targetTariff) {
|
||||
onClose();
|
||||
onExpiredFallback(targetTariff);
|
||||
queryClient.invalidateQueries({ queryKey: ['purchase-options', subscriptionId] });
|
||||
}
|
||||
// Backend signal: this subscription can't be switched (it lapsed, or it's
|
||||
// a trial). Hand the selected tariff back to the parent so it opens the
|
||||
// regular purchase form instead.
|
||||
if (shouldUsePurchaseFlow(error)) {
|
||||
const targetTariff = tariffs.find((tariff) => tariff.id === tariffId);
|
||||
if (targetTariff) {
|
||||
onClose();
|
||||
onExpiredFallback(targetTariff);
|
||||
queryClient.invalidateQueries({ queryKey: ['purchase-options', subscriptionId] });
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -213,15 +225,10 @@ export function SwitchTariffSheet({
|
||||
|
||||
{switchMutation.isError &&
|
||||
(() => {
|
||||
// Suppress the toast when the subscription_expired
|
||||
// fallback already triggered: the parent is now
|
||||
// showing the regular purchase form, surfacing the
|
||||
// raw axios message here would be misleading.
|
||||
const detail =
|
||||
switchMutation.error instanceof AxiosError
|
||||
? switchMutation.error.response?.data?.detail
|
||||
: null;
|
||||
if (typeof detail === 'object' && detail?.error_code === 'subscription_expired') {
|
||||
// Suppress the toast when the purchase-flow fallback already
|
||||
// triggered (expired / trial): the parent is now showing the
|
||||
// regular purchase form, so the raw axios message would mislead.
|
||||
if (shouldUsePurchaseFlow(switchMutation.error)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user