Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/app/dashboard/subscriptions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { validateUserSession } from '@/utils/supabase/server';
import { Subscription } from '@paddle/paddle-node-sdk';
import { revalidatePath } from 'next/cache';
import { getPaddleInstance } from '@/utils/paddle/get-paddle-instance';
import { getCustomerId } from '@/utils/paddle/get-customer-id';

const paddle = getPaddleInstance();

Expand All @@ -15,11 +16,19 @@ export async function cancelSubscription(subscriptionId: string): Promise<Subscr
try {
await validateUserSession();

const subscription = await paddle.subscriptions.cancel(subscriptionId, { effectiveFrom: 'next_billing_period' });
if (subscription) {
const customerId = await getCustomerId();
const subscription = await paddle.subscriptions.get(subscriptionId);
if (!customerId || subscription.customerId !== customerId) {
return { error: 'Something went wrong, please try again later' };
}

const canceledSubscription = await paddle.subscriptions.cancel(subscriptionId, {
effectiveFrom: 'next_billing_period',
});
if (canceledSubscription) {
revalidatePath('/dashboard/subscriptions');
}
return JSON.parse(JSON.stringify(subscription));
return JSON.parse(JSON.stringify(canceledSubscription));
} catch (e) {
console.log('Error canceling subscription', e);
return { error: 'Something went wrong, please try again later' };
Expand Down
4 changes: 4 additions & 0 deletions src/utils/paddle/get-subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export async function getSubscription(subscriptionId: string): Promise<Subscript
include: ['next_transaction', 'recurring_transaction_details'],
});

if (subscription.customerId !== customerId) {
return { error: ErrorMessage };
}

return { data: parseSDKResponse(subscription) };
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
32 changes: 32 additions & 0 deletions supabase/migrations/20260721000000_restrict_read_policies.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- Ensure row level security is enforced on billing tables
alter table "public"."customers" enable row level security;

alter table "public"."subscriptions" enable row level security;

-- Replace the permissive read policies that exposed all rows to any authenticated user
drop policy if exists "Enable read access for authenticated users to customers" on "public"."customers";

drop policy if exists "Enable read access for authenticated users to subscriptions" on "public"."subscriptions";

-- Authenticated users can only read the customer row matching their own email
create policy "Enable users to read their own customer record" on "public"."customers" as PERMISSIVE for SELECT to authenticated using (
(
select
auth.jwt () ->> 'email'
) = email
);

-- Authenticated users can only read subscriptions linked to their own customer record
create policy "Enable users to read their own subscriptions" on "public"."subscriptions" as PERMISSIVE for SELECT to authenticated using (
customer_id in (
select
customer_id
from
public.customers
where
email = (
select
auth.jwt () ->> 'email'
)
)
);
Loading