Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
🛠️ PHP + MySQL REST API  ·  Lesson 07

Cart API

Cart के rules

  1. 1सिर्फ logged-in users का cart होता है (require_auth())
  2. 2हर user के हर product की एक row — दोबारा "Add" दबाने पर quantity बढ़ती है
  3. 3Quantity 1 से 10 के बीच, और stock से कभी ज़्यादा नहीं
  4. 4Prices और totals हमेशा server पर calculate होते हैं, app कभी नहीं भेजती
  5. 5Subtotal ₹499 या ज़्यादा हो तो free delivery, वरना ₹40 (config.php में)
  6. 6Hidden या out-of-stock items दिखते रहते हैं पर available: false mark होते हैं और गिने नहीं जाते
API क्या guarantee करती है
9:41▮▮ 85%☰Cart⋮🎉 इस order पर FREE delivery⌚Smart Watch Pro₹1,899− 1 +📘Learn Kotlin in 30 Days₹349 × 2− 1 +Price (3 items)₹5,997Product discount− ₹3,400Total amount₹2,597Checkout🏠Home🗂️Categories♡Wishlist🛒Cart👤Account
इस screen का हर number एक cart response से आता है

Step 1: includes/cart.php

तीन endpoints (cart, coupons, orders) को वही calculations चाहिए, इसलिए वे एक shared file में हैं। cart_rows() cart को live product data से जोड़ता है; cart_summary() response बनाता है; coupon_check() Coupons lesson में समझाया गया है।

PHP – backend/includes/cart.php
<?php
// Cart and coupon calculations, shared by cart/list, coupons/apply and orders/place.
// The server always calculates prices itself: never trust a total sent by the app.

/** Cart rows of a user with live product price and stock. */
function cart_rows($userId)
{
    $stmt = db()->prepare(
        'SELECT c.product_id, c.qty, p.name, p.image, p.price, p.mrp, p.stock, p.is_active
         FROM cart_items c JOIN products p ON p.id = c.product_id
         WHERE c.user_id = ? ORDER BY c.id DESC'
    );
    $stmt->execute(array($userId));
    return $stmt->fetchAll();
}

/**
 * Check a coupon against a subtotal.
 * Returns array('ok' => bool, 'message' => string, 'discount' => float).
 */
function coupon_check($code, $subtotal)
{
    $code = strtoupper(trim($code));
    if ($code === '') {
        return array('ok' => false, 'message' => 'Enter a coupon code.', 'discount' => 0);
    }
    $stmt = db()->prepare('SELECT * FROM coupons WHERE code = ? AND is_active = 1');
    $stmt->execute(array($code));
    $c = $stmt->fetch();
    if (!$c) {
        return array('ok' => false, 'message' => 'Invalid coupon code.', 'discount' => 0);
    }
    if ($c['expires_at'] !== null && strtotime($c['expires_at']) < time()) {
        return array('ok' => false, 'message' => 'This coupon has expired.', 'discount' => 0);
    }
    if ($subtotal < (float) $c['min_order']) {
        return array('ok' => false,
            'message' => 'Add items worth ' . rs($c['min_order'] - $subtotal) . ' more to use ' . $code . '.',
            'discount' => 0);
    }
    if ($c['type'] === 'percent') {
        $discount = $subtotal * (float) $c['value'] / 100;
        if ($c['max_discount'] !== null) {
            $discount = min($discount, (float) $c['max_discount']);
        }
    } else {
        $discount = (float) $c['value'];
    }
    $discount = money(min($discount, $subtotal));
    return array('ok' => true, 'message' => 'Coupon applied! You saved ' . rs($discount) . '.', 'discount' => $discount, 'code' => $code);
}

/** Full cart with totals. Pass a coupon code to include its discount. */
function cart_summary($userId, $couponCode = '')
{
    $items = array();
    $subtotal = 0;
    $mrpTotal = 0;
    $count = 0;
    foreach (cart_rows($userId) as $r) {
        $available = ((int) $r['is_active'] === 1) && ((int) $r['stock'] >= (int) $r['qty']);
        $line = (float) $r['price'] * (int) $r['qty'];
        if ($available) {
            $subtotal += $line;
            $mrpTotal += (float) $r['mrp'] * (int) $r['qty'];
            $count += (int) $r['qty'];
        }
        $items[] = array(
            'product_id' => (int) $r['product_id'],
            'name'       => $r['name'],
            'image'      => img_url($r['image']),
            'price'      => (float) $r['price'],
            'mrp'        => (float) $r['mrp'],
            'qty'        => (int) $r['qty'],
            'stock'      => (int) $r['stock'],
            'line_total' => money($line),
            'available'  => $available,
        );
    }
    $discount = 0;
    $couponMsg = null;
    $appliedCode = null;
    if ($couponCode !== '' && $subtotal > 0) {
        $res = coupon_check($couponCode, $subtotal);
        $couponMsg = $res['message'];
        if ($res['ok']) {
            $discount = $res['discount'];
            $appliedCode = $res['code'];
        }
    }
    $delivery = ($subtotal == 0 || $subtotal >= FREE_DELIVERY_ABOVE) ? 0 : DELIVERY_FEE;
    return array(
        'items'          => $items,
        'item_count'     => $count,
        'mrp_total'      => money($mrpTotal),
        'subtotal'       => money($subtotal),
        'you_save'       => money($mrpTotal - $subtotal + $discount),
        'discount'       => money($discount),
        'coupon_code'    => $appliedCode,
        'coupon_message' => $couponMsg,
        'delivery_fee'   => money($delivery),
        'total'          => money($subtotal - $discount + $delivery),
    );
}

Step 2: cart/add.php

Add पहले stock और cart में पहले से रखी quantity पढ़ता है, limits check करता है, फिर एक "upsert" query चलाता है:

SQL – upsert
-- First tap on "Add": no row yet → INSERT
-- Second tap: row exists (UNIQUE user_id + product_id) → UPDATE qty instead
INSERT INTO cart_items (user_id, product_id, qty) VALUES (1, 7, 2)
ON DUPLICATE KEY UPDATE qty = VALUES(qty);
PHP – backend/api/cart/add.php
<?php
// POST {product_id, qty}  -> adds qty to the cart (creates the row if needed)
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/cart.php';
require_method('POST');
$user = require_auth();

$productId = in_int('product_id');
$qty = max(1, in_int('qty', 1));

$stmt = db()->prepare('SELECT stock FROM products WHERE id = ? AND is_active = 1');
$stmt->execute(array($productId));
$stock = $stmt->fetchColumn();
if ($stock === false) {
    fail('Product not found.', 404);
}

$cur = db()->prepare('SELECT qty FROM cart_items WHERE user_id = ? AND product_id = ?');
$cur->execute(array($user['id'], $productId));
$newQty = (int) $cur->fetchColumn() + $qty;

if ($newQty > (int) $stock) {
    fail('Only ' . (int) $stock . ' left in stock.', 409);
}
if ($newQty > 10) {
    fail('You can buy at most 10 of one product.', 409);
}

// One query for both cases thanks to the UNIQUE(user_id, product_id) key.
db()->prepare(
    'INSERT INTO cart_items (user_id, product_id, qty) VALUES (?, ?, ?)
     ON DUPLICATE KEY UPDATE qty = VALUES(qty)'
)->execute(array($user['id'], $productId, $newQty));

ok(cart_summary($user['id']), 'Added to cart.');
require_auth()token नहीं तो 401product है?hidden / गलत id हो तो 404new qty = cart में + qtyजैसे 1 + 1 = 2≤ stock और ≤ 10?409 "Only 12 left in stock."INSERT … ON DUPLICATE KEYनई और पुरानी दोनों के लिए एक querycart_summary() लौटाएँitems + totals
add.php step by step

Step 3: update.php और remove.php

update.php exact quantity set करता है (− / + buttons के लिए)। qty: 0 भेजने पर item हट जाता है। remove.php row delete करता है (swipe to delete)।

PHP – backend/api/cart/update.php
<?php
// POST {product_id, qty}  -> sets the exact qty (qty 0 removes the item)
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/cart.php';
require_method('POST');
$user = require_auth();

$productId = in_int('product_id');
$qty = in_int('qty');

if ($qty <= 0) {
    db()->prepare('DELETE FROM cart_items WHERE user_id = ? AND product_id = ?')->execute(array($user['id'], $productId));
    ok(cart_summary($user['id']), 'Item removed.');
}

$stmt = db()->prepare('SELECT stock FROM products WHERE id = ? AND is_active = 1');
$stmt->execute(array($productId));
$stock = $stmt->fetchColumn();
if ($stock === false) {
    fail('Product not found.', 404);
}
if ($qty > (int) $stock) {
    fail('Only ' . (int) $stock . ' left in stock.', 409);
}
if ($qty > 10) {
    fail('You can buy at most 10 of one product.', 409);
}

$upd = db()->prepare('UPDATE cart_items SET qty = ? WHERE user_id = ? AND product_id = ?');
$upd->execute(array($qty, $user['id'], $productId));
if ($upd->rowCount() === 0) {
    // Not in cart yet (or same qty) -> make sure the row exists
    db()->prepare(
        'INSERT INTO cart_items (user_id, product_id, qty) VALUES (?, ?, ?)
         ON DUPLICATE KEY UPDATE qty = VALUES(qty)'
    )->execute(array($user['id'], $productId, $qty));
}
ok(cart_summary($user['id']), 'Cart updated.');
PHP – backend/api/cart/remove.php
<?php
// POST {product_id}
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/cart.php';
require_method('POST');
$user = require_auth();

db()->prepare('DELETE FROM cart_items WHERE user_id = ? AND product_id = ?')
    ->execute(array($user['id'], in_int('product_id')));
ok(cart_summary($user['id']), 'Item removed.');

Step 4: cart/list.php

सिर्फ तीन lines, क्योंकि काम cart_summary() करता है। Optional ?coupon= से checkout coupon लगाकर cart reload कर सकता है।

PHP – backend/api/cart/list.php
<?php
// GET api/cart/list.php?coupon=SAVE10   (coupon is optional)
require_once __DIR__ . '/../../includes/helpers.php';
require_once __DIR__ . '/../../includes/cart.php';
require_method('GET');
$user = require_auth();

ok(cart_summary($user['id'], q_str('coupon')));

Totals कैसे calculate होते हैं

FieldFormulaExample
mrp_totalΣ mrp × qty4999 + 499 × 2 = 5,997
subtotalΣ price × qty1899 + 349 × 2 = 2,597
discountcoupon discount (बिना coupon 0)0
delivery_feesubtotal ≥ 499 तो 0, वरना 400
totalsubtotal − discount + delivery_fee2,597
you_savemrp_total − subtotal + discount3,400
⚠️ App से आया price कभी trust न करें

कोई भी tool से API call करके "price": 1 भेज सकता है। इसीलिए add.php सिर्फ product_id और qty लेता है, और हर रुपया database से calculate होता है।

असली responses

पहला product जोड़ें
POST /karteasy/api/cart/add.php Authorization: Bearer 3f9c…e1a7 {"product_id": 7, "qty": 1} HTTP 200 { "success": true, "message": "Added to cart.", "data": { "items": [ { "product_id": 7, "name": "Smart Watch Pro", "image": "https://yourdomain.com/karteasy/uploads/products/p7.png", "price": 1899, "mrp": 4999, "qty": 1, "stock": 35, "line_total": 1899, "available": true } ], "item_count": 1, "mrp_total": 4999, "subtotal": 1899, "you_save": 3100, "discount": 0, "coupon_code": null, "coupon_message": null, "delivery_fee": 0, "total": 1899 } }
दूसरा product जोड़ें (qty 2)
POST /karteasy/api/cart/add.php Authorization: Bearer 3f9c…e1a7 {"product_id": 13, "qty": 2} HTTP 200 { "success": true, "message": "Added to cart.", "data": { "items": [ { "product_id": 13, "name": "Learn Kotlin in 30 Days", "image": "https://yourdomain.com/karteasy/uploads/products/p13.png", "price": 349, "mrp": 499, "qty": 2, "stock": 70, "line_total": 698, "available": true }, { "product_id": 7, "name": "Smart Watch Pro", "image": "https://yourdomain.com/karteasy/uploads/products/p7.png", "price": 1899, "mrp": 4999, "qty": 1, "stock": 35, "line_total": 1899, "available": true } ], "item_count": 3, "mrp_total": 5997, "subtotal": 2597, "you_save": 3400, "discount": 0, "coupon_code": null, "coupon_message": null, "delivery_fee": 0, "total": 2597 } }
बहुत ज़्यादा
POST /karteasy/api/cart/add.php Authorization: Bearer 3f9c…e1a7 {"product_id": 16, "qty": 13} HTTP 409 { "success": false, "message": "Only 12 left in stock.", "data": null }
− button से quantity बदलें
POST /karteasy/api/cart/update.php Authorization: Bearer 3f9c…e1a7 {"product_id": 13, "qty": 1} HTTP 200 { "success": true, "message": "Cart updated.", "data": { "items": [ { "product_id": 13, "name": "Learn Kotlin in 30 Days", "image": "https://yourdomain.com/karteasy/uploads/products/p13.png", "price": 349, "mrp": 499, "qty": 1, "stock": 70, "line_total": 349, "available": true }, { "product_id": 7, "name": "Smart Watch Pro", "image": "https://yourdomain.com/karteasy/uploads/products/p7.png", "price": 1899, "mrp": 4999, "qty": 1, "stock": 35, "line_total": 1899, "available": true } ], "item_count": 2, "mrp_total": 5498, "subtotal": 2248, "you_save": 3250, "discount": 0, "coupon_code": null, "coupon_message": null, "delivery_fee": 0, "total": 2248 } }

अक्सर पूछे जाने वाले प्रश्न (FAQ)

हर cart call पूरा cart क्यों लौटाती है?
Add, update या remove के बाद app को नए totals चाहिए ही होते हैं। पूरा cart लौटाने से दूसरी request बचती है और badge, list और price box हमेशा server के साथ sync रहते हैं।
Item cart में रहते हुए price बदल जाए तो?
Cart table सिर्फ product_id और qty रखती है। Prices हर बार products से पढ़े जाते हैं, इसलिए cart हमेशा आज का price दिखाता है और order उसी से बनता है।
क्या guests (बिना login) का cart होना चाहिए?
KartEasy पहले login करवाता है, जिससे API simple रहती है। Guest cart के लिए items phone में (DataStore/Room) रखें और login के बाद cart/add.php को भेज दें।
← E-commerce App Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicWishlist और Address API आपकी प्रगति: 7 / 28 (25%) · Lesson 8 / 28 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें