Start learning free

LESSON 120 OF 188 · BACKEND: PAYMENTS AND OTHER SERVICES

Project: Checkout

Practice

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. db.execute(...) gives a cursor, and its .lastrowid is the id of the row the INSERT just added. For example, on another table:

customer_id = db.execute("INSERT INTO customers (name, email) VALUES (?, ?)", ("Ion Popa", "ion@example.com")).lastrowid
db.commit()

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. 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 find_order and pay_order already do.


Hint
Check for an empty cart before any INSERT. Keep the new order's id from .lastrowid, and once the items are saved, find_order(order_id)["total"] is the amount to charge.


STEP 1 OF 8
    Ctrl + Enter to check