Update database schema and tests for subscription and order management
This commit includes a significant change in the way subscriptions and orders are handled. It introduces the ability to update existing orders and subscriptions, including adding and deleting items. The diff also adds checks to ensure that only valid items can be read, and adds corresponding test cases to verify these changes.
This commit is contained in:
@@ -1595,7 +1595,8 @@ on conflict (
|
||||
trial_ends_at = excluded.trial_ends_at
|
||||
returning
|
||||
* into new_subscription;
|
||||
-- Upsert subscription items
|
||||
|
||||
-- Upsert subscription items and delete ones that are not in the line_items array
|
||||
with item_data as (
|
||||
select
|
||||
(line_item ->> 'id')::varchar as line_item_id,
|
||||
@@ -1607,7 +1608,19 @@ on conflict (
|
||||
(line_item ->> 'interval')::varchar as intv,
|
||||
(line_item ->> 'interval_count')::integer as intv_count
|
||||
from
|
||||
jsonb_array_elements(line_items) as line_item)
|
||||
jsonb_array_elements(line_items) as line_item
|
||||
),
|
||||
line_item_ids as (
|
||||
select line_item_id from item_data
|
||||
),
|
||||
deleted_items as (
|
||||
delete from
|
||||
public.subscription_items
|
||||
where
|
||||
public.subscription_items.subscription_id = new_subscription.id
|
||||
and public.subscription_items.id not in (select line_item_id from line_item_ids)
|
||||
returning *
|
||||
)
|
||||
insert into public.subscription_items(
|
||||
id,
|
||||
subscription_id,
|
||||
@@ -1964,6 +1977,28 @@ on conflict (
|
||||
returning
|
||||
* into new_order;
|
||||
|
||||
-- Upsert order items and delete ones that are not in the line_items array
|
||||
with item_data as (
|
||||
select
|
||||
(line_item ->> 'id')::varchar as line_item_id,
|
||||
(line_item ->> 'product_id')::varchar as prod_id,
|
||||
(line_item ->> 'variant_id')::varchar as var_id,
|
||||
(line_item ->> 'price_amount')::numeric as price_amt,
|
||||
(line_item ->> 'quantity')::integer as qty
|
||||
from
|
||||
jsonb_array_elements(line_items) as line_item
|
||||
),
|
||||
line_item_ids as (
|
||||
select line_item_id from item_data
|
||||
),
|
||||
deleted_items as (
|
||||
delete from
|
||||
public.order_items
|
||||
where
|
||||
public.order_items.order_id = new_order.id
|
||||
and public.order_items.id not in (select line_item_id from line_item_ids)
|
||||
returning *
|
||||
)
|
||||
insert into public.order_items(
|
||||
id,
|
||||
order_id,
|
||||
@@ -1972,18 +2007,18 @@ on conflict (
|
||||
price_amount,
|
||||
quantity)
|
||||
select
|
||||
(line_item ->> 'id')::varchar,
|
||||
line_item_id,
|
||||
target_order_id,
|
||||
(line_item ->> 'product_id')::varchar,
|
||||
(line_item ->> 'variant_id')::varchar,
|
||||
(line_item ->> 'price_amount')::numeric,
|
||||
(line_item ->> 'quantity')::integer
|
||||
prod_id,
|
||||
var_id,
|
||||
price_amt,
|
||||
qty
|
||||
from
|
||||
jsonb_array_elements(line_items) as line_item
|
||||
on conflict (id)
|
||||
do update set
|
||||
price_amount = excluded.price_amount,
|
||||
quantity = excluded.quantity;
|
||||
item_data
|
||||
on conflict (id)
|
||||
do update set
|
||||
price_amount = excluded.price_amount,
|
||||
quantity = excluded.quantity;
|
||||
|
||||
return new_order;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (tests.get_supabase_uid('primary_owner'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_order function
|
||||
SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}]');
|
||||
SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}, {"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 1}]');
|
||||
|
||||
-- Verify that the order was created correctly
|
||||
SELECT is(
|
||||
@@ -24,37 +24,42 @@ SELECT is(
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(1::bigint),
|
||||
row(2::bigint),
|
||||
'The order items should be created'
|
||||
);
|
||||
|
||||
-- Call the upsert_order function again to update the order
|
||||
SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]');
|
||||
select public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]');
|
||||
|
||||
-- Verify that the order was updated correctly
|
||||
SELECT is(
|
||||
(SELECT status FROM public.orders WHERE id = 'order_test'),
|
||||
select is(
|
||||
(select status FROM public.orders WHERE id = 'order_test'),
|
||||
'succeeded',
|
||||
'The order status should be succeeded'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
select row_eq(
|
||||
$$ select quantity from order_items where variant_id = 'var_test' $$,
|
||||
row(10::int),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from order_items where id = 'order_item_2' $$,
|
||||
'The order item should be deleted when the order is updated'
|
||||
);
|
||||
|
||||
select tests.authenticate_as('primary_owner');
|
||||
|
||||
-- account can read their own subscription
|
||||
SELECT isnt_empty(
|
||||
select isnt_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account can read their own order'
|
||||
);
|
||||
|
||||
SELECT isnt_empty(
|
||||
select isnt_empty(
|
||||
$$ select * from order_items where order_id = 'order_test' $$,
|
||||
'The account can read their own order'
|
||||
'The account can read their own orders items'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
@@ -62,17 +67,18 @@ select tests.create_supabase_user('foreigner');
|
||||
select tests.authenticate_as('foreigner');
|
||||
|
||||
-- account cannot read other's subscription
|
||||
SELECT is_empty(
|
||||
select is_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account cannot read the other account orders'
|
||||
);
|
||||
|
||||
SELECT is_empty(
|
||||
select is_empty(
|
||||
$$ select 1 from order_items where order_id = 'order_test' $$,
|
||||
'The account cannot read the other account order items'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
SELECT * FROM finish();
|
||||
ROLLBACK;
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
|
||||
|
||||
@@ -33,13 +33,23 @@ SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_789",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_3",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
row(3::bigint),
|
||||
'The subscription items should be created'
|
||||
);
|
||||
|
||||
@@ -95,24 +105,34 @@ SELECT row_eq(
|
||||
);
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
select is(
|
||||
(select active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
false,
|
||||
'The subscription should be inactive'
|
||||
);
|
||||
|
||||
SELECT is(
|
||||
(SELECT status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
select is(
|
||||
(select status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
'past_due',
|
||||
'The subscription status should be past_due'
|
||||
);
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from public.subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account can read their own subscription items'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.subscription_items where subscription_id = 'sub_test' and variant_id = 'var_test_3' $$,
|
||||
'The subscription items should be deleted when the subscription is updated and the item is missing'
|
||||
);
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]');
|
||||
select public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]');
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
select is(
|
||||
(select active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
true,
|
||||
'The subscription should be active'
|
||||
);
|
||||
@@ -120,14 +140,14 @@ SELECT is(
|
||||
select tests.authenticate_as('primary_owner');
|
||||
|
||||
-- account can read their own subscription
|
||||
SELECT isnt_empty(
|
||||
select isnt_empty(
|
||||
$$ select 1 from subscriptions where id = 'sub_test' $$,
|
||||
'The account can read their own subscription'
|
||||
);
|
||||
|
||||
SELECT isnt_empty(
|
||||
select is_empty(
|
||||
$$ select * from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account can read their own subscription items'
|
||||
'No subscription items should be returned when the subscription is empty'
|
||||
);
|
||||
|
||||
-- users cannot manually update subscriptions
|
||||
|
||||
@@ -12,7 +12,12 @@ INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (makerkit.get_account_id_by_slug('makerkit'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_order function
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1}]');
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_3", "product_id": "prod_test", "variant_id": "var_test_3", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_4", "product_id": "prod_test", "variant_id": "var_test_4", "price_amount": 100, "quantity": 1}
|
||||
]');
|
||||
|
||||
-- Verify that the order was created correctly
|
||||
SELECT is(
|
||||
@@ -24,12 +29,22 @@ SELECT is(
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(1::bigint),
|
||||
row(4::bigint),
|
||||
'The order items should be created'
|
||||
);
|
||||
|
||||
-- Call the upsert_order function again to update the order
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10}]');
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 10},
|
||||
{"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 200, "quantity": 1}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(2::bigint),
|
||||
'The order items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the order was updated correctly
|
||||
SELECT is(
|
||||
@@ -44,6 +59,12 @@ SELECT row_eq(
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select price_amount from order_items where variant_id = 'var_test_2' $$,
|
||||
row(200::numeric),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
select tests.authenticate_as('member');
|
||||
|
||||
-- account can read their own subscription
|
||||
|
||||
@@ -33,13 +33,23 @@ SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), '
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_789",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_3",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
row(3::bigint),
|
||||
'The subscription items should be created'
|
||||
);
|
||||
|
||||
@@ -80,6 +90,12 @@ SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), '
|
||||
}
|
||||
]');
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select price_amount from subscription_items where variant_id = 'var_test' $$,
|
||||
@@ -107,6 +123,16 @@ SELECT is(
|
||||
'The subscription status should be past_due'
|
||||
);
|
||||
|
||||
select tests.authenticate_as('member');
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
'The member can also read the subscription items'
|
||||
);
|
||||
|
||||
set role service_role;
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]');
|
||||
|
||||
@@ -125,9 +151,9 @@ select isnt_empty(
|
||||
'The account can read their own subscription'
|
||||
);
|
||||
|
||||
select isnt_empty(
|
||||
select is_empty(
|
||||
$$ select * from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account can read their own subscription items'
|
||||
'The subscription items are now empty'
|
||||
);
|
||||
|
||||
select is(
|
||||
|
||||
Reference in New Issue
Block a user