Remove unused subscription-related frontend code (#12557)

This commit is contained in:
Shruti1128
2026-03-02 01:44:00 +05:30
committed by GitHub
parent 1e6a92b454
commit d6b8d80026
10 changed files with 22 additions and 330 deletions

View File

@@ -1,8 +1,4 @@
import { openHands } from "../open-hands-axios";
import {
CancelSubscriptionResponse,
SubscriptionAccess,
} from "./billing.types";
/**
* Billing Service API - Handles all billing-related API endpoints
@@ -44,41 +40,6 @@ class BillingService {
);
return data.credits;
}
/**
* Get the user's subscription access information
* @returns The user's subscription access details or null if not available
*/
static async getSubscriptionAccess(): Promise<SubscriptionAccess | null> {
const { data } = await openHands.get<SubscriptionAccess | null>(
"/api/billing/subscription-access",
);
return data;
}
/**
* Create a subscription checkout session for subscribing to a plan
* @returns The redirect URL for the subscription checkout session
*/
static async createSubscriptionCheckoutSession(): Promise<{
redirect_url?: string;
}> {
const { data } = await openHands.post(
"/api/billing/subscription-checkout-session",
);
return data;
}
/**
* Cancel the user's subscription
* @returns The response indicating the result of the cancellation request
*/
static async cancelSubscription(): Promise<CancelSubscriptionResponse> {
const { data } = await openHands.post<CancelSubscriptionResponse>(
"/api/billing/cancel-subscription",
);
return data;
}
}
export default BillingService;

View File

@@ -1,12 +0,0 @@
export type SubscriptionAccess = {
start_at: string;
end_at: string;
created_at: string;
cancelled_at?: string | null;
stripe_subscription_id?: string | null;
};
export interface CancelSubscriptionResponse {
status: string;
message: string;
}

View File

@@ -1,83 +0,0 @@
import React from "react";
import { useTranslation, Trans } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import { BrandButton } from "#/components/features/settings/brand-button";
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
import { useCancelSubscription } from "#/hooks/mutation/use-cancel-subscription";
import {
displayErrorToast,
displaySuccessToast,
} from "#/utils/custom-toast-handlers";
interface CancelSubscriptionModalProps {
isOpen: boolean;
onClose: () => void;
endDate?: string;
}
export function CancelSubscriptionModal({
isOpen,
onClose,
endDate,
}: CancelSubscriptionModalProps) {
const { t } = useTranslation();
const cancelSubscriptionMutation = useCancelSubscription();
const handleCancelSubscription = async () => {
try {
await cancelSubscriptionMutation.mutateAsync();
displaySuccessToast(t(I18nKey.PAYMENT$SUBSCRIPTION_CANCELLED));
onClose();
} catch {
displayErrorToast(t(I18nKey.ERROR$GENERIC));
}
};
if (!isOpen) return null;
return (
<ModalBackdrop>
<div
data-testid="cancel-subscription-modal"
className="bg-base-secondary p-6 rounded-xl flex flex-col gap-4 border border-tertiary w-[500px]"
>
<h3 className="text-xl font-bold">
{t(I18nKey.PAYMENT$CANCEL_SUBSCRIPTION_TITLE)}
</h3>
<p className="text-sm">
{endDate ? (
<Trans
i18nKey={I18nKey.PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE_WITH_DATE}
values={{ date: endDate }}
components={{ date: <span className="underline" /> }}
/>
) : (
t(I18nKey.PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE)
)}
</p>
<div className="w-full flex gap-2 mt-2">
<BrandButton
testId="confirm-cancel-button"
type="button"
variant="primary"
className="grow"
onClick={handleCancelSubscription}
isDisabled={cancelSubscriptionMutation.isPending}
>
{t(I18nKey.BUTTON$CONFIRM)}
</BrandButton>
<BrandButton
testId="modal-cancel-button"
type="button"
variant="secondary"
className="grow"
onClick={onClose}
isDisabled={cancelSubscriptionMutation.isPending}
>
{t(I18nKey.BUTTON$CANCEL)}
</BrandButton>
</div>
</div>
</ModalBackdrop>
);
}

View File

@@ -1,12 +0,0 @@
import { useMutation } from "@tanstack/react-query";
import BillingService from "#/api/billing-service/billing-service.api";
export const useCreateSubscriptionCheckoutSession = () =>
useMutation({
mutationFn: BillingService.createSubscriptionCheckoutSession,
onSuccess: (data) => {
if (data.redirect_url) {
window.location.href = data.redirect_url;
}
},
});

View File

@@ -1,16 +0,0 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import BillingService from "#/api/billing-service/billing-service.api";
export const useCancelSubscription = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: BillingService.cancelSubscription,
onSuccess: () => {
// Invalidate subscription access query to refresh the UI
queryClient.invalidateQueries({
queryKey: ["user", "subscription_access"],
});
},
});
};

View File

@@ -1,18 +0,0 @@
import { useQuery } from "@tanstack/react-query";
import { useConfig } from "./use-config";
import BillingService from "#/api/billing-service/billing-service.api";
import { useIsOnTosPage } from "#/hooks/use-is-on-tos-page";
export const useSubscriptionAccess = () => {
const { data: config } = useConfig();
const isOnTosPage = useIsOnTosPage();
return useQuery({
queryKey: ["user", "subscription_access"],
queryFn: BillingService.getSubscriptionAccess,
enabled:
!isOnTosPage &&
config?.app_mode === "saas" &&
config?.feature_flags?.enable_billing,
});
};

View File

@@ -574,10 +574,7 @@ export enum I18nKey {
PAYMENT$MANAGE_CREDITS = "PAYMENT$MANAGE_CREDITS",
PAYMENT$CANCEL_SUBSCRIPTION = "PAYMENT$CANCEL_SUBSCRIPTION",
PAYMENT$CANCEL_SUBSCRIPTION_TITLE = "PAYMENT$CANCEL_SUBSCRIPTION_TITLE",
PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE = "PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE",
PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE_WITH_DATE = "PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE_WITH_DATE",
PAYMENT$SUBSCRIPTION_CANCELLED = "PAYMENT$SUBSCRIPTION_CANCELLED",
PAYMENT$SUBSCRIPTION_CANCELLED_EXPIRES = "PAYMENT$SUBSCRIPTION_CANCELLED_EXPIRES",
PAYMENT$NEXT_BILLING_DATE = "PAYMENT$NEXT_BILLING_DATE",
WAITLIST$IF_NOT_JOINED = "WAITLIST$IF_NOT_JOINED",
WAITLIST$PATIENCE_MESSAGE = "WAITLIST$PATIENCE_MESSAGE",

View File

@@ -9183,38 +9183,7 @@
"de": "Abonnement kündigen",
"uk": "Скасувати підписку"
},
"PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE": {
"en": "Are you sure you want to cancel your subscription? You will lose access to premium features at the end of your current billing period.",
"ja": "サブスクリプションをキャンセルしてもよろしいですか?現在の請求期間の終了時にプレミアム機能へのアクセスを失います。",
"zh-CN": "您确定要取消订阅吗?您将在当前计费周期结束时失去对高级功能的访问权限。",
"zh-TW": "您確定要取消訂閱嗎?您將在當前計費週期結束時失去對高級功能的訪問權限。",
"ko-KR": "구독을 취소하시겠습니까? 현재 결제 기간이 끝나면 프리미엄 기능에 대한 액세스를 잃게 됩니다.",
"no": "Er du sikker på at du vil avbryte abonnementet? Du vil miste tilgang til premium-funksjoner ved slutten av gjeldende faktureringsperiode.",
"it": "Sei sicuro di voler annullare l'abbonamento? Perderai l'accesso alle funzionalità premium alla fine del periodo di fatturazione corrente.",
"pt": "Tem certeza de que deseja cancelar sua assinatura? Você perderá o acesso aos recursos premium no final do período de cobrança atual.",
"es": "¿Estás seguro de que quieres cancelar tu suscripción? Perderás el acceso a las funciones premium al final de tu período de facturación actual.",
"ar": "هل أنت متأكد من أنك تريد إلغاء اشتراكك؟ ستفقد الوصول إلى الميزات المميزة في نهاية فترة الفوترة الحالية.",
"fr": "Êtes-vous sûr de vouloir annuler votre abonnement ? Vous perdrez l'accès aux fonctionnalités premium à la fin de votre période de facturation actuelle.",
"tr": "Aboneliğinizi iptal etmek istediğinizden emin misiniz? Mevcut faturalama döneminizin sonunda premium özelliklere erişiminizi kaybedeceksiniz.",
"de": "Sind Sie sicher, dass Sie Ihr Abonnement kündigen möchten? Sie verlieren den Zugang zu Premium-Funktionen am Ende Ihres aktuellen Abrechnungszeitraums.",
"uk": "Ві впевнені, що хочете скасувати підписку? Ви втратите доступ до преміум-функцій наприкінці поточного розрахункового періоду."
},
"PAYMENT$CANCEL_SUBSCRIPTION_MESSAGE_WITH_DATE": {
"en": "Are you sure you want to cancel your subscription? You will lose access to premium features on {{date}}.",
"ja": "サブスクリプションをキャンセルしてもよろしいですか?{{date}}にプレミアム機能へのアクセスを失います。",
"zh-CN": "您确定要取消订阅吗?您将在{{date}}失去对高级功能的访问权限。",
"zh-TW": "您確定要取消訂閱嗎?您將在{{date}}失去對高級功能的訪問權限。",
"ko-KR": "구독을 취소하시겠습니까? {{date}}에 프리미엄 기능에 대한 액세스를 잃게 됩니다.",
"no": "Er du sikker på at du vil avbryte abonnementet? Du vil miste tilgang til premium-funksjoner {{date}}.",
"it": "Sei sicuro di voler annullare l'abbonamento? Perderai l'accesso alle funzionalità premium il {{date}}.",
"pt": "Tem certeza de que deseja cancelar sua assinatura? Você perderá o acesso aos recursos premium em {{date}}.",
"es": "¿Estás seguro de que quieres cancelar tu suscripción? Perderás el acceso a las funciones premium el {{date}}.",
"ar": "هل أنت متأكد من أنك تريد إلغاء اشتراكك؟ ستفقد الوصول إلى الميزات المميزة في {{date}}.",
"fr": "Êtes-vous sûr de vouloir annuler votre abonnement ? Vous perdrez l'accès aux fonctionnalités premium le {{date}}.",
"tr": "Aboneliğinizi iptal etmek istediğinizden emin misiniz? {{date}} tarihinde premium özelliklere erişiminizi kaybedeceksiniz.",
"de": "Sind Sie sicher, dass Sie Ihr Abonnement kündigen möchten? Sie verlieren den Zugang zu Premium-Funktionen am {{date}}.",
"uk": "Ви впевнені, що хочете скасувати підписку? Ви втратите доступ до преміум-функцій {{date}}."
},
"PAYMENT$SUBSCRIPTION_CANCELLED": {
"en": "Subscription cancelled successfully",
"ja": "サブスクリプションが正常にキャンセルされました",
@@ -9231,22 +9200,7 @@
"de": "Abonnement erfolgreich gekündigt",
"uk": "Підписку успішно скасовано"
},
"PAYMENT$SUBSCRIPTION_CANCELLED_EXPIRES": {
"en": "Your subscription has been cancelled and will expire on {{date}}",
"ja": "サブスクリプションはキャンセルされ、{{date}}に期限切れになります",
"zh-CN": "您的订阅已取消,将于{{date}}到期",
"zh-TW": "您的訂閱已取消,將於{{date}}到期",
"ko-KR": "구독이 취소되었으며 {{date}}에 만료됩니다",
"no": "Abonnementet ditt er avbrutt og utløper {{date}}",
"it": "Il tuo abbonamento è stato annullato e scadrà il {{date}}",
"pt": "Sua assinatura foi cancelada e expirará em {{date}}",
"es": "Tu suscripción ha sido cancelada y expirará el {{date}}",
"ar": "تم إلغاء اشتراكك وسينتهي في {{date}}",
"fr": "Votre abonnement a été annulé et expirera le {{date}}",
"tr": "Aboneliğiniz iptal edildi ve {{date}} tarihinde sona erecek",
"de": "Ihr Abonnement wurde gekündigt und läuft am {{date}} ab",
"uk": "Ваша підписка скасована і закінчиться {{date}}"
},
"PAYMENT$NEXT_BILLING_DATE": {
"en": "Next billing date: {{date}}",
"ja": "次回請求日: {{date}}",

View File

@@ -1,101 +1,22 @@
import { delay, http, HttpResponse } from "msw";
import { SubscriptionAccess } from "#/api/billing-service/billing.types";
// Mock data for different subscription scenarios
const MOCK_ACTIVE_SUBSCRIPTION: SubscriptionAccess = {
start_at: "2024-01-01T00:00:00Z",
end_at: "2024-12-31T23:59:59Z",
created_at: "2024-01-01T00:00:00Z",
cancelled_at: null,
stripe_subscription_id: "sub_mock123456789",
};
export const BILLING_HANDLERS = [
http.get("/api/billing/credits", async () => {
await delay();
return HttpResponse.json({ credits: "100" });
}),
const MOCK_CANCELLED_SUBSCRIPTION: SubscriptionAccess = {
start_at: "2024-01-01T00:00:00Z",
end_at: "2025-01-01T23:59:59Z",
created_at: "2024-01-01T00:00:00Z",
cancelled_at: "2024-06-15T10:30:00Z",
stripe_subscription_id: "sub_mock123456789",
};
http.post("/api/billing/create-checkout-session", async () => {
await delay();
return HttpResponse.json({
redirect_url: "https://stripe.com/some-checkout",
});
}),
// Expired subscription (end_at < now) - will be filtered out by backend logic
const MOCK_EXPIRED_SUBSCRIPTION: SubscriptionAccess = {
start_at: "2024-01-01T00:00:00Z",
end_at: "2024-06-01T00:00:00Z", // Expired
created_at: "2024-01-01T00:00:00Z",
cancelled_at: null,
stripe_subscription_id: "sub_mock123456789",
};
// Helper function to check if subscription is currently active (matches backend logic)
function isSubscriptionActive(
subscription: SubscriptionAccess | null,
): boolean {
if (!subscription) return false;
const now = new Date();
const startAt = new Date(subscription.start_at);
const endAt = new Date(subscription.end_at);
// Backend filters: status == 'ACTIVE' AND start_at <= now AND end_at >= now
return startAt <= now && endAt >= now;
}
// Factory function to create billing handlers with different subscription states
function createBillingHandlers(subscriptionData: SubscriptionAccess | null) {
return [
http.get("/api/billing/credits", async () => {
await delay();
return HttpResponse.json({ credits: "100" });
}),
http.get("/api/billing/subscription-access", async () => {
await delay();
// Apply backend filtering logic - only return if subscription is currently active
const activeSubscription = isSubscriptionActive(subscriptionData)
? subscriptionData
: null;
return HttpResponse.json(activeSubscription);
}),
http.post("/api/billing/create-checkout-session", async () => {
await delay();
return HttpResponse.json({
redirect_url: "https://stripe.com/some-checkout",
});
}),
http.post("/api/billing/subscription-checkout-session", async () => {
await delay();
return HttpResponse.json({
redirect_url: "https://stripe.com/some-subscription-checkout",
});
}),
http.post("/api/billing/create-customer-setup-session", async () => {
await delay();
return HttpResponse.json({
redirect_url: "https://stripe.com/some-customer-setup",
});
}),
http.post("/api/billing/cancel-subscription", async () => {
await delay();
return HttpResponse.json({
status: "success",
message: "Subscription cancelled successfully",
});
}),
];
}
// Export different handler sets for different testing scenarios
export const STRIPE_BILLING_HANDLERS = createBillingHandlers(
MOCK_ACTIVE_SUBSCRIPTION,
);
export const STRIPE_BILLING_HANDLERS_NO_SUBSCRIPTION =
createBillingHandlers(null);
export const STRIPE_BILLING_HANDLERS_CANCELLED_SUBSCRIPTION =
createBillingHandlers(MOCK_CANCELLED_SUBSCRIPTION);
export const STRIPE_BILLING_HANDLERS_EXPIRED_SUBSCRIPTION =
createBillingHandlers(MOCK_EXPIRED_SUBSCRIPTION); // This will return null due to filtering
http.post("/api/billing/create-customer-setup-session", async () => {
await delay();
return HttpResponse.json({
redirect_url: "https://stripe.com/some-customer-setup",
});
}),
];

View File

@@ -1,5 +1,5 @@
import { API_KEYS_HANDLERS } from "./api-keys-handlers";
import { STRIPE_BILLING_HANDLERS } from "./billing-handlers";
import { BILLING_HANDLERS } from "./billing-handlers";
import { FILE_SERVICE_HANDLERS } from "./file-service-handlers";
import { TASK_SUGGESTIONS_HANDLERS } from "./task-suggestions-handlers";
import { SECRETS_HANDLERS } from "./secrets-handlers";
@@ -16,7 +16,7 @@ import { ANALYTICS_HANDLERS } from "./analytics-handlers";
export const handlers = [
...API_KEYS_HANDLERS,
...STRIPE_BILLING_HANDLERS,
...BILLING_HANDLERS,
...FILE_SERVICE_HANDLERS,
...TASK_SUGGESTIONS_HANDLERS,
...SECRETS_HANDLERS,