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 an array of items, with no prices: {"customer_id": 5, "items": [{"product_id": 3, "quantity": 2}, {"product_id": 6, "quantity": 1}]}. It arrives as JSON, and express.json() on the route reads it into req.body. 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. .run(...) gives back an object, and its .lastInsertRowid is the id of the row the INSERT just added. Its type is number | bigint, so wrap it in Number(...) to get a plain number. For example, on another table:
const customerId = Number(db.prepare("INSERT INTO customers (name, email) VALUES (?, ?)").run("Ion Popa", "ion@example.com").lastInsertRowid);
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: add POST /checkout, with an async function. Refuse an empty cart with 400, save the order and its items with the shop's prices, charge the 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 build the /checkout route step by step, reusing what findOrder and the pay route already do.
Hint
Check for an empty cart before any INSERT. Keep the new order's id from Number(... .lastInsertRowid), and once the items are saved, findOrder(orderId).total is the amount to charge. Express never answers for you: until the route sends the receipt, a paying client just keeps waiting.
Free plan: 30 tutor messages. PRO is unlimited.
