diff --git a/backend/src/controllers/carts.c b/backend/src/controllers/carts.c index f4b8fbf..0dcd38b 100644 --- a/backend/src/controllers/carts.c +++ b/backend/src/controllers/carts.c @@ -27,6 +27,10 @@ void route_post_carts_purchase(HttpCtx* ctx) size_t item_amount = req.items.size; + // accumulate product_prices and total + + int64_t total_dkk_cent = 0; + ProductPriceVec prices; product_price_vec_construct(&prices); @@ -38,9 +42,34 @@ void route_post_carts_purchase(HttpCtx* ctx) RESPOND_SERVER_ERROR(ctx); goto l0_return; } + total_dkk_cent += price.price_dkk_cent; product_price_vec_push(&prices, price); } + // check and update user balance + + User user; + DbRes db_res = db_user_with_id(cx->db, &user, session->user_id); + if (db_res != DbRes_Ok) { + RESPOND_SERVER_ERROR(ctx); + goto l0_return; + } + + if (user.balance_dkk_cent < total_dkk_cent) { + RESPOND_JSON( + ctx, 200, "{\"ok\":false,\"message\":\"insufficient funds\"}"); + goto l0_return; + } + user.balance_dkk_cent -= total_dkk_cent; + + db_res = db_user_update(cx->db, &user); + if (db_res != DbRes_Ok) { + RESPOND_SERVER_ERROR(ctx); + goto l0_return; + } + + // assemble receipt + Receipt receipt = { .id = 0, .user_id = session->user_id, @@ -59,8 +88,10 @@ void route_post_carts_purchase(HttpCtx* ctx) }); } + // insert receipt + int64_t receipt_id; - DbRes db_res = db_receipt_insert(cx->db, &receipt, &receipt_id); + db_res = db_receipt_insert(cx->db, &receipt, &receipt_id); if (db_res != DbRes_Ok) { RESPOND_SERVER_ERROR(ctx); goto l0_return; @@ -71,5 +102,6 @@ void route_post_carts_purchase(HttpCtx* ctx) l0_return: receipt_destroy(&receipt); product_price_vec_destroy(&prices); + user_destroy(&user); carts_purchase_req_destroy(&req); } diff --git a/backend/test/test.ts b/backend/test/test.ts index 0b80856..97af7f5 100644 --- a/backend/test/test.ts +++ b/backend/test/test.ts @@ -49,6 +49,8 @@ Deno.test("test backend", async (t) => { assertEquals(sessionUserRes.ok, true); }); + const user1 = await sessionUser(token); + await t.step("test /api/users/balance/add", async () => { const sessionUserRes = await post<{ ok: boolean }>( "/api/users/balance/add", @@ -60,6 +62,9 @@ Deno.test("test backend", async (t) => { assertEquals(sessionUserRes.ok, true); }); + const user2 = await sessionUser(token); + assertNotEquals(user1.balance_dkk_cent, user2.balance_dkk_cent); + await testCartsAndReceipts(t, token!); await t.step("test /api/sessions/logout", async () => { @@ -76,6 +81,8 @@ Deno.test("test backend", async (t) => { async function testCartsAndReceipts(t: Deno.TestContext, token: string) { let receiptId: number | undefined = undefined; + const user1 = await sessionUser(token); + await t.step("test /api/carts/purchase", async () => { const res = await post<{ ok: boolean; receipt_id: number }>( "/api/carts/purchase", @@ -92,6 +99,9 @@ async function testCartsAndReceipts(t: Deno.TestContext, token: string) { receiptId = res.receipt_id; }); + const user2 = await sessionUser(token); + assertNotEquals(user1.balance_dkk_cent, user2.balance_dkk_cent); + if (!receiptId) { return; } @@ -125,6 +135,24 @@ async function testCartsAndReceipts(t: Deno.TestContext, token: string) { }); } +type SessionUser = { id: number; email: string; balance_dkk_cent: number }; + +async function sessionUser( + token: string, +): Promise { + const res = await get<{ + ok: boolean; + user: SessionUser; + }>( + "/api/sessions/user", + { "Session-Token": token! }, + ); + if (!res.ok) { + throw new Error(); + } + return res.user; +} + function get( path: string, headers: Record,