Point billing clients at /monetization/ instead of /finance/.
Unit Tests / test (pull_request) Failing after 11s
Unit Tests / test (pull_request) Failing after 11s
Align FE with backend finance→monetization rename (api/monetization); backend keeps /api/finance as alias only.
This commit is contained in:
@@ -4,10 +4,10 @@
|
||||
|
||||
| Runtime | Checkout / manage | Ledger |
|
||||
|---------|-------------------|--------|
|
||||
| Web (`!isNativePlatform`) | Stripe Checkout + Customer Portal via `/finance/...` | Stripe webhooks |
|
||||
| Web (`!isNativePlatform`) | Stripe Checkout + Customer Portal via `/monetization/...` | Stripe webhooks |
|
||||
| Native Capacitor | RevenueCat IAP (`@revenuecat/purchases-capacitor`) | RevenueCat webhooks → same invoice/payment tables |
|
||||
|
||||
FE displays whatever `/finance/invoices/` (and subscription) returns after refresh. Provider badge uses `provider` + `revenuecat_store`.
|
||||
FE displays whatever `/monetization/invoices/` (and subscription) returns after refresh. Provider badge uses `provider` + `revenuecat_store`.
|
||||
|
||||
## App user id
|
||||
|
||||
|
||||
@@ -157,16 +157,16 @@ const mockFinanceGets = ({
|
||||
plans?: unknown[];
|
||||
} = {}) => {
|
||||
mockGet.mockImplementation((url: string) => {
|
||||
if (url === '/finance/invoices/') {
|
||||
if (url === '/monetization/invoices/') {
|
||||
return Promise.resolve({ data: invoices });
|
||||
}
|
||||
if (url === '/finance/payments/') {
|
||||
if (url === '/monetization/payments/') {
|
||||
return Promise.resolve({ data: payments });
|
||||
}
|
||||
if (url === '/finance/subscription/') {
|
||||
if (url === '/monetization/subscription/') {
|
||||
return Promise.resolve({ data: subscription });
|
||||
}
|
||||
if (url === '/finance/plans/') {
|
||||
if (url === '/monetization/plans/') {
|
||||
return Promise.resolve({ data: plans });
|
||||
}
|
||||
return Promise.reject(new Error(`unexpected GET ${url}`));
|
||||
@@ -246,7 +246,7 @@ describe('BillingSection', () => {
|
||||
await user.click(screen.getByRole('button', { name: /Change plan/i }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockPost).toHaveBeenCalledWith('/finance/portal/', {
|
||||
expect(mockPost).toHaveBeenCalledWith('/monetization/portal/', {
|
||||
return_url: 'http://localhost/account/',
|
||||
});
|
||||
});
|
||||
@@ -321,7 +321,7 @@ describe('BillingSection', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockPost).toHaveBeenCalledWith(
|
||||
'/finance/checkout/',
|
||||
'/monetization/checkout/',
|
||||
expect.objectContaining({
|
||||
success_url: expect.stringContaining('/billing/success'),
|
||||
cancel_url: expect.stringContaining('/billing/cancel'),
|
||||
@@ -378,7 +378,7 @@ describe('BillingSection', () => {
|
||||
expect(mockPurchasePlan).toHaveBeenCalled();
|
||||
});
|
||||
expect(mockPost).not.toHaveBeenCalledWith(
|
||||
'/finance/checkout/',
|
||||
'/monetization/checkout/',
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
@@ -292,10 +292,10 @@ const BillingSection = (): JSX.Element => {
|
||||
try {
|
||||
const [invoiceResponse, paymentResponse, subscriptionResponse, plansResponse] =
|
||||
await Promise.all([
|
||||
axiosInstance.get<FinanceInvoice[]>('/finance/invoices/'),
|
||||
axiosInstance.get<FinancePayment[]>('/finance/payments/'),
|
||||
axiosInstance.get<SubscriptionMe>('/finance/subscription/'),
|
||||
axiosInstance.get<SubscriptionPlanInfo[]>('/finance/plans/'),
|
||||
axiosInstance.get<FinanceInvoice[]>('/monetization/invoices/'),
|
||||
axiosInstance.get<FinancePayment[]>('/monetization/payments/'),
|
||||
axiosInstance.get<SubscriptionMe>('/monetization/subscription/'),
|
||||
axiosInstance.get<SubscriptionPlanInfo[]>('/monetization/plans/'),
|
||||
]);
|
||||
setInvoices(Array.isArray(invoiceResponse.data) ? invoiceResponse.data : []);
|
||||
setPayments(Array.isArray(paymentResponse.data) ? paymentResponse.data : []);
|
||||
@@ -385,7 +385,7 @@ const BillingSection = (): JSX.Element => {
|
||||
try {
|
||||
const returnUrl = `${window.location.origin}/account/`;
|
||||
const response = await axiosInstance.post<{ portal_url: string }>(
|
||||
'/finance/portal/',
|
||||
'/monetization/portal/',
|
||||
{ return_url: returnUrl }
|
||||
);
|
||||
const portalUrl = response.data?.portal_url;
|
||||
@@ -461,7 +461,7 @@ const BillingSection = (): JSX.Element => {
|
||||
...(planSlug ? { plan_slug: planSlug } : {}),
|
||||
});
|
||||
const response = await axiosInstance.post<{ checkout_url: string }>(
|
||||
'/finance/checkout/',
|
||||
'/monetization/checkout/',
|
||||
{
|
||||
success_url,
|
||||
cancel_url,
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('Header2 (#81 subscription-aware Documents nav link)', () => {
|
||||
|
||||
renderHeader();
|
||||
|
||||
await waitFor(() => expect(mockGet).toHaveBeenCalledWith('/finance/subscription/'));
|
||||
await waitFor(() => expect(mockGet).toHaveBeenCalledWith('/monetization/subscription/'));
|
||||
expect(screen.queryByText('Documents')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -174,8 +174,8 @@ const UsageSummaryCard = (): JSX.Element => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [subscriptionResponse, invoiceResponse] = await Promise.all([
|
||||
axiosInstance.get<SubscriptionMe>('/finance/subscription/'),
|
||||
axiosInstance.get<FinanceInvoice[]>('/finance/invoices/'),
|
||||
axiosInstance.get<SubscriptionMe>('/monetization/subscription/'),
|
||||
axiosInstance.get<FinanceInvoice[]>('/monetization/invoices/'),
|
||||
]);
|
||||
setSubscription(subscriptionResponse.data || null);
|
||||
setHasPortalAccess(
|
||||
@@ -231,7 +231,7 @@ const UsageSummaryCard = (): JSX.Element => {
|
||||
try {
|
||||
const returnUrl = `${window.location.origin}/account/`;
|
||||
const response = await axiosInstance.post<{ portal_url: string }>(
|
||||
'/finance/portal/',
|
||||
'/monetization/portal/',
|
||||
{ return_url: returnUrl }
|
||||
);
|
||||
if (response.data?.portal_url) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { planAllowsRag, SubscriptionMe } from '../utils/finance';
|
||||
|
||||
/**
|
||||
* Module-level cache so every component that calls useSubscription() during the
|
||||
* same session shares one /finance/subscription/ request instead of each
|
||||
* same session shares one /monetization/subscription/ request instead of each
|
||||
* mounting its own (e.g. Header2 + DocumentStoragePage on the same page).
|
||||
*/
|
||||
let cachedSubscription: SubscriptionMe | null = null;
|
||||
@@ -18,7 +18,7 @@ async function loadSubscription(): Promise<SubscriptionMe | null> {
|
||||
}
|
||||
if (!inFlightRequest) {
|
||||
inFlightRequest = axiosInstance
|
||||
.get<SubscriptionMe>('/finance/subscription/')
|
||||
.get<SubscriptionMe>('/monetization/subscription/')
|
||||
.then((response: AxiosResponse<SubscriptionMe>) => {
|
||||
cachedSubscription = response.data || null;
|
||||
return cachedSubscription;
|
||||
@@ -44,7 +44,7 @@ export type UseSubscriptionResult = {
|
||||
refresh: () => Promise<void>;
|
||||
};
|
||||
|
||||
/** Fetches /finance/subscription/ once per session (shared across callers) and exposes plan-derived flags. */
|
||||
/** Fetches /monetization/subscription/ once per session (shared across callers) and exposes plan-derived flags. */
|
||||
export function useSubscription(): UseSubscriptionResult {
|
||||
const { authenticated } = useContext(AuthContext);
|
||||
const [subscription, setSubscription] = useState<SubscriptionMe | null>(cachedSubscription);
|
||||
|
||||
@@ -133,7 +133,7 @@ describe('AuthCallback', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockPost).toHaveBeenCalledWith(
|
||||
'/finance/checkout/',
|
||||
'/monetization/checkout/',
|
||||
expect.objectContaining({
|
||||
success_url: expect.stringContaining('/billing/success'),
|
||||
cancel_url: expect.stringContaining('/billing/cancel'),
|
||||
|
||||
@@ -181,7 +181,7 @@ const AuthCallback = (): JSX.Element => {
|
||||
setStatusText('Starting checkout…');
|
||||
const { success_url, cancel_url } = checkoutReturnUrls();
|
||||
trackEvent(AnalyticsEvents.CHECKOUT_STARTED, { source: 'sso_signup' });
|
||||
const checkoutResponse = await axiosInstance.post('/finance/checkout/', {
|
||||
const checkoutResponse = await axiosInstance.post('/monetization/checkout/', {
|
||||
success_url,
|
||||
cancel_url,
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ const subscriptionResponse = (rag: boolean) => ({
|
||||
|
||||
const mockGetByUrl = (overrides: { rag: boolean }) => {
|
||||
mockGet.mockImplementation((url: string) => {
|
||||
if (url === '/finance/subscription/') {
|
||||
if (url === '/monetization/subscription/') {
|
||||
return Promise.resolve(subscriptionResponse(overrides.rag));
|
||||
}
|
||||
if (typeof url === 'string' && url.startsWith('/documents/')) {
|
||||
|
||||
@@ -139,7 +139,7 @@ describe('SignUp', () => {
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockPost).toHaveBeenCalledWith('/finance/checkout/', {
|
||||
expect(mockPost).toHaveBeenCalledWith('/monetization/checkout/', {
|
||||
success_url: 'http://localhost/billing/success?session_id={CHECKOUT_SESSION_ID}',
|
||||
cancel_url: 'http://localhost/billing/cancel',
|
||||
});
|
||||
|
||||
@@ -271,7 +271,7 @@ const SignUp = (): JSX.Element => {
|
||||
|
||||
const { success_url, cancel_url } = checkoutReturnUrls();
|
||||
trackEvent(AnalyticsEvents.CHECKOUT_STARTED, { source: 'signup' });
|
||||
const checkoutResponse = await axiosInstance.post('/finance/checkout/', {
|
||||
const checkoutResponse = await axiosInstance.post('/monetization/checkout/', {
|
||||
success_url,
|
||||
cancel_url,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user