Skip to main content

Overview

The order processing system in Mis Compras handles the conversion of shopping cart items into database orders with full transaction tracking. Orders are stored with customer information, product details, quantities, and pricing.

Key Features

Order Creation

Convert cart items into database orders

Order Details

Track individual products within each order

Transaction Records

Timestamped order history for auditing

User Association

Link orders to customer accounts

Database Schema

Orders Table

The main orders table stores order headers:
CREATE TABLE `pedidos` (
  `id_pedido` int NOT NULL AUTO_INCREMENT,
  `id_usuario` int DEFAULT NULL,
  `total` decimal(10,2) NOT NULL,
  `direccion` varchar(255) DEFAULT NULL,
  `fecha_pedido` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id_pedido`),
  FOREIGN KEY (`id_usuario`) REFERENCES `usuarios` (`id_usuario`) 
    ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Order Details Table

The details table stores line items for each order:
CREATE TABLE `detalle_pedido` (
  `id_detalle` int NOT NULL AUTO_INCREMENT,
  `id_pedido` int NOT NULL,
  `id_producto` int NOT NULL,
  `cantidad` int DEFAULT '1',
  `precio` decimal(10,2) NOT NULL,
  PRIMARY KEY (`id_detalle`),
  FOREIGN KEY (`id_pedido`) REFERENCES `pedidos` (`id_pedido`) 
    ON DELETE CASCADE ON UPDATE CASCADE,
  FOREIGN KEY (`id_producto`) REFERENCES `productos` (`id_producto`) 
    ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
The precio field in detalle_pedido stores the price at the time of purchase, preserving historical pricing even if product prices change later.

Checkout Process

Order Creation Flow

1

Receive cart data

Client sends cart items, total, and user info as JSON
2

Validate input

Server validates structure and required fields
3

Create order record

Insert main order into pedidos table
4

Insert order details

Loop through items and insert into detalle_pedido
5

Return success

Send confirmation back to client

Backend Implementation

The checkout endpoint (php/checkout.php) processes orders:
php/checkout.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

header('Content-Type: application/json');
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");

include '../conexion.php';

// Read JSON body
$input = file_get_contents("php://input");
$data = json_decode($input, true);

// Validate structure
if (empty($data) || !isset($data['items']) || !is_array($data['items'])) {
    echo json_encode(['success' => false, 'message' => 'Estructura JSON inválida']);
    exit;
}

// Extract data
$id_usuario = isset($data['usuario_id']) ? intval($data['usuario_id']) : null;
$total = isset($data['total']) ? floatval($data['total']) : 0;
$items = $data['items'];

// Insert order
$stmt = $conn->prepare("INSERT INTO pedidos (id_usuario, total) VALUES (?, ?)");
$stmt->bind_param("id", $id_usuario, $total);

if (!$stmt->execute()) {
    echo json_encode([
        'success' => false, 
        'message' => 'Error al registrar pedido: ' . $stmt->error
    ]);
    exit;
}

$id_pedido = $stmt->insert_id;

// Insert order details
$stmt = $conn->prepare(
    "INSERT INTO detalle_pedido (id_pedido, id_producto, cantidad, precio) VALUES (?, ?, ?, ?)"
);

foreach ($items as $item) {
    $id_producto = intval($item['id']);
    $cantidad = intval($item['cantidad']);
    $precio = floatval($item['precio']);

    $stmt->bind_param("iiid", $id_pedido, $id_producto, $cantidad, $precio);
    
    if (!$stmt->execute()) {
        echo json_encode([
            'success' => false, 
            'message' => 'Error al registrar compra: ' . $stmt->error
        ]);
        exit;
    }
}

// Success response
echo json_encode([
    'success' => true,
    'message' => 'Compra registrada correctamente.',
    'redirect' => 'gracias.html'
]);

$conn->close();
?>

Request/Response Format

Checkout Request

The client sends cart data as JSON:
POST /php/checkout.php
Content-Type: application/json

{
  "usuario_id": 17,
  "total": "2599.98",
  "direccion": "123 Main St, City, State 12345",
  "items": [
    {
      "id": "1",
      "nombre": "iPhone 15 Pro",
      "precio": 999.99,
      "cantidad": 2,
      "subtotal": 1999.98,
      "imagen": "iphone15.jpg"
    },
    {
      "id": "3",
      "nombre": "iPad",
      "precio": 599.99,
      "cantidad": 1,
      "subtotal": 599.99,
      "imagen": "ipad.jpg"
    }
  ]
}

Success Response

{
  "success": true,
  "message": "Compra registrada correctamente.",
  "redirect": "gracias.html"
}

Error Response

{
  "success": false,
  "message": "Error al registrar pedido: Connection timeout"
}

Data Validation

Input Validation

The checkout endpoint validates:
1

JSON structure

Ensure valid JSON with required fields
2

Items array

Verify items array exists and is not empty
3

Data types

Cast user ID, product IDs, quantities to integers
4

Price format

Convert prices to float values

Validation Example

// Structure validation
if (empty($data) || !isset($data['items']) || !is_array($data['items'])) {
    echo json_encode(['success' => false, 'message' => 'Estructura JSON inválida']);
    exit;
}

// Type casting for safety
$id_usuario = isset($data['usuario_id']) ? intval($data['usuario_id']) : null;
$total = isset($data['total']) ? floatval($data['total']) : 0;
$id_producto = intval($item['id']);
$cantidad = intval($item['cantidad']);
$precio = floatval($item['precio']);
Always validate and sanitize input data on the server side, even if client-side validation is in place.

Order Data Model

Order Record

Each order in the pedidos table represents a complete purchase:
[
  'id_pedido' => 24,
  'id_usuario' => 17,
  'total' => 2599.98,
  'direccion' => '123 Main St, City, State 12345',
  'fecha_pedido' => '2025-11-20 01:35:21'
]

Order Detail Records

Each product in the order has a detail record:
[
  'id_detalle' => 21,
  'id_pedido' => 24,
  'id_producto' => 1,
  'cantidad' => 2,
  'precio' => 999.99
]

Transaction Management

Database Transactions

For production systems, wrap order creation in a transaction:
// Start transaction
$conn->begin_transaction();

try {
    // Insert order
    $stmt = $conn->prepare("INSERT INTO pedidos (id_usuario, total) VALUES (?, ?)");
    $stmt->bind_param("id", $id_usuario, $total);
    $stmt->execute();
    $id_pedido = $stmt->insert_id;

    // Insert order details
    $stmt = $conn->prepare(
        "INSERT INTO detalle_pedido (id_pedido, id_producto, cantidad, precio) VALUES (?, ?, ?, ?)"
    );
    
    foreach ($items as $item) {
        $id_producto = intval($item['id']);
        $cantidad = intval($item['cantidad']);
        $precio = floatval($item['precio']);
        
        $stmt->bind_param("iiid", $id_pedido, $id_producto, $cantidad, $precio);
        $stmt->execute();
    }

    // Commit transaction
    $conn->commit();
    
    echo json_encode(['success' => true]);
} catch (Exception $e) {
    // Rollback on error
    $conn->rollback();
    echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
Transactions ensure that either the complete order is created (header + all details) or nothing is saved, maintaining database consistency.

Guest Checkout

Handling Anonymous Orders

The system allows guest checkout where id_usuario can be NULL:
$id_usuario = isset($data['usuario_id']) ? intval($data['usuario_id']) : null;

// This will insert NULL if user is not logged in
$stmt->bind_param("id", $id_usuario, $total);

Database Constraint

FOREIGN KEY (`id_usuario`) REFERENCES `usuarios` (`id_usuario`) 
  ON DELETE SET NULL ON UPDATE CASCADE
This constraint allows:
  • Orders without users (guest checkout)
  • Orders to persist if user account is deleted (set to NULL)

Order History

Querying User Orders

Retrieve all orders for a specific user:
<?php
$id_usuario = $_GET['id'] ?? null;

if (!$id_usuario) {
    echo json_encode(['success' => false, 'message' => 'User ID required']);
    exit;
}

$sql = "SELECT p.id_pedido, p.total, p.fecha_pedido, p.direccion
        FROM pedidos p
        WHERE p.id_usuario = ?
        ORDER BY p.fecha_pedido DESC";

$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id_usuario);
$stmt->execute();
$result = $stmt->get_result();

$orders = [];
while ($row = $result->fetch_assoc()) {
    $orders[] = $row;
}

echo json_encode(['success' => true, 'orders' => $orders]);
?>

Getting Order Details

Retrieve all products in a specific order:
<?php
$id_pedido = $_GET['id'] ?? null;

$sql = "SELECT 
          dp.id_detalle,
          dp.cantidad,
          dp.precio,
          p.nombre,
          p.imagen
        FROM detalle_pedido dp
        INNER JOIN productos p ON dp.id_producto = p.id_producto
        WHERE dp.id_pedido = ?";

$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id_pedido);
$stmt->execute();
$result = $stmt->get_result();

$items = [];
while ($row = $result->fetch_assoc()) {
    $items[] = $row;
}

echo json_encode(['success' => true, 'items' => $items]);
?>

Complete Order View

Join Query for Full Order

Get complete order information with all products:
SELECT 
    p.id_pedido,
    p.total,
    p.fecha_pedido,
    p.direccion,
    u.nombre as cliente_nombre,
    u.email as cliente_email,
    dp.cantidad,
    dp.precio as precio_unitario,
    (dp.cantidad * dp.precio) as subtotal,
    prod.nombre as producto_nombre,
    prod.imagen as producto_imagen
FROM pedidos p
LEFT JOIN usuarios u ON p.id_usuario = u.id_usuario
INNER JOIN detalle_pedido dp ON p.id_pedido = dp.id_pedido
INNER JOIN productos prod ON dp.id_producto = prod.id_producto
WHERE p.id_pedido = ?
ORDER BY dp.id_detalle;

Error Handling

Common Error Scenarios

Cause: Malformed JSON or missing required fieldsResponse:
{
  "success": false,
  "message": "Estructura JSON inválida"
}
Cause: Cannot connect to MySQL databaseResponse:
{
  "success": false,
  "message": "Error al registrar pedido: Connection refused"
}
Cause: Items array is emptyHandled client-side:
if (!cart.length) {
  alert('Tu carrito está vacío.');
  return;
}
Cause: Product ID doesn’t exist in databaseResponse:
{
  "success": false,
  "message": "Error al registrar compra: Foreign key constraint fails"
}

Debugging

Debug Logging

The checkout script includes debug logging:
php/checkout.php
// Log raw input for debugging
$input = file_get_contents("php://input");
file_put_contents("debug_input.txt", $input);

$data = json_decode($input, true);
This creates a debug_input.txt file with the raw POST body, useful for troubleshooting.

Checking for Errors

error_reporting(E_ALL);
ini_set('display_errors', 1);
Disable display_errors in production environments. Log errors to files instead of displaying them to users.

Post-Checkout Flow

Client-Side Success Handling

After successful checkout:
1

Clear cart

Remove all items from localStorage
2

Show confirmation

Display success modal to user
3

Redirect

Navigate to thank you page after 3 seconds
carrito.js
if (data.success) {
  // Clear cart
  this.clear();

  // Show success modal
  const modal = document.createElement('div');
  modal.innerHTML = `
    <div style="...">
      <h2>¡Compra realizada con éxito!</h2>
      <p>Tu pedido ha sido registrado correctamente.</p>
    </div>`;
  document.body.appendChild(modal);

  // Redirect
  setTimeout(() => {
    window.location.href = 'gracias.html';
  }, 3000);
}

Sample Database Records

Example Order

-- pedidos table
INSERT INTO pedidos (id_pedido, id_usuario, total, direccion, fecha_pedido)
VALUES (24, 17, 999.99, NULL, '2025-11-20 01:35:21');

-- detalle_pedido table
INSERT INTO detalle_pedido (id_detalle, id_pedido, id_producto, cantidad, precio)
VALUES (21, 24, 1, 1, 999.99);
This represents:
  • Order #24
  • Customer ID 17
  • Total $999.99
  • 1 iPhone 15 Pro at $999.99

API Reference

POST /php/checkout.php

Process cart items and create an order. Content-Type: application/json Request Body:
{
  "usuario_id": 17,          // integer or null for guest
  "total": "2599.98",        // string or number
  "direccion": "...",        // string or null
  "items": [                 // array (required)
    {
      "id": "1",             // product ID (string or number)
      "cantidad": 2,         // integer
      "precio": 999.99,      // number
      "nombre": "...",       // string (not stored)
      "subtotal": 1999.98,   // number (not stored)
      "imagen": "..."        // string (not stored)
    }
  ]
}
Success Response (200):
{
  "success": true,
  "message": "Compra registrada correctamente.",
  "redirect": "gracias.html"
}
Error Response (200):
{
  "success": false,
  "message": "Error al registrar pedido: ..."
}

Best Practices

Use Transactions

Wrap order creation in database transactions for consistency

Preserve Prices

Store product prices in order details, not just product IDs

Validate Input

Always validate and sanitize all input data

Log Errors

Log errors to files for debugging, don’t expose to users

Next Steps

Shopping Cart

Learn how the cart feeds data to checkout

User Authentication

Understand user session management

Build docs developers (and LLMs) love