So far a purchase took several requests: the order was already in the database, POST /orders/{id}/pay charged it, and the worker sent the email later. A shop's checkout does it all in one request: the client sends the cart, and the shop creates the order, takes the payment, queues the email and answers with a receipt.
The cart has a customer and a list of items, with no prices: {"customer_id": 5, "items": [{"product_id": 3, "quantity": 2}, {"product_id": 6, "quantity": 1}]}. The shop takes each price from its own products table, never from the client, who could send any price. The order is saved as 'pending' before the payment, so a declined order stays and can be paid later.
The database picks a new row's id. An INSERT followed by SELECT last_insert_rowid() in the same command hands it back through ExecuteScalar(). For example, on another table:
var cmd = new SqliteCommand("INSERT INTO customers (name, email) VALUES ($name, $email); SELECT last_insert_rowid()", db);
cmd.Parameters.AddWithValue("$name", "Ion Popa");
cmd.Parameters.AddWithValue("$email", "ion@example.com");
var customerId = Convert.ToInt32(cmd.ExecuteScalar());
Your code must answer, in this order:
POST /checkout customer 5: product 3 ×2, product 6 ×1 → 201 {"order_id": 12, "payment_id": "pay_1", "status": "paid", "total": 80}
POST /checkout customer 2: no items → 400 {"error": "The cart is empty"}
POST /checkout customer 4: product 8 ×25 → 402 {"error": "Payment declined", "order_id": 13}
POST /checkout customer 1: product 4 ×3, product 5 ×1 → 201 {"order_id": 14, "payment_id": "pay_2", "status": "paid", "total": 21}
POST /admin/run-jobs → 200 {"ran": 2}
The empty cart saves nothing. The 25 gift cards cost 1250, over the provider's limit of 1000, so order 13 stays 'pending'. No email goes out during a checkout: only when the worker runs do the customers of orders 12 and 14 get their "Your order … is paid" email.
Your task: finish POST /checkout. The empty-cart check (400), the new order and its items with the shop's prices are already written. Charge the order's total, answer 402 if the card is declined, and otherwise mark the order paid, queue its email job and answer the receipt with 201.
Assignment
Follow the Coding Tutorial to finish the /checkout route step by step, reusing what FindOrder and the pay route already do.
Hint
Once the items are saved, FindOrder(orderId)["total"] is the amount to charge. The route has no return at its end until your last step, so C# won't compile it before then (CS1643): that's expected.
Free plan: 30 tutor messages. PRO is unlimited.
