session_start(); date_default_timezone_set('America/Lima'); define('BASE_PATH', __DIR__); if (file_exists(BASE_PATH . '/config.php')) { require BASE_PATH . '/config.php'; } if (!defined('APP_NAME')) define('APP_NAME', 'JAMA Store'); if (!defined('APP_URL')) define('APP_URL', ''); if (!defined('ADMIN_EMAIL')) define('ADMIN_EMAIL', 'admin@jama.pe'); if (!defined('ADMIN_PASSWORD')) define('ADMIN_PASSWORD', 'jhoan14'); if (!defined('RESET_ADMIN_PASSWORD')) define('RESET_ADMIN_PASSWORD', false); if (!defined('YAPE_PHONE')) define('YAPE_PHONE', '+51 906264890'); if (!defined('YAPE_RECIPIENT')) define('YAPE_RECIPIENT', 'JULIO MIN*'); if (!defined('CONTACT_PHONE')) define('CONTACT_PHONE', 'REDES SOCIALES'); if (!defined('CONTACT_EMAIL')) define('CONTACT_EMAIL', 'ventas@jama.pe'); if (!defined('STORE_CITY')) define('STORE_CITY', 'Peru'); if (!defined('GOOGLE_CLIENT_ID')) define('GOOGLE_CLIENT_ID', ''); if (!defined('GOOGLE_CLIENT_SECRET')) define('GOOGLE_CLIENT_SECRET', ''); if (!defined('GOOGLE_REDIRECT_URI')) define('GOOGLE_REDIRECT_URI', ''); if (!defined('DATA_DIR')) define('DATA_DIR', BASE_PATH . '/data'); if (!defined('DB_PATH')) define('DB_PATH', DATA_DIR . '/jama_store.sqlite'); if (!defined('UPLOAD_PRODUCTS_DIR')) define('UPLOAD_PRODUCTS_DIR', BASE_PATH . '/uploads/products'); if (!defined('UPLOAD_SOCIAL_DIR')) define('UPLOAD_SOCIAL_DIR', BASE_PATH . '/uploads/social'); if (!defined('RECEIPTS_DIR')) define('RECEIPTS_DIR', BASE_PATH . '/private/receipts'); foreach (array(DATA_DIR, UPLOAD_PRODUCTS_DIR, UPLOAD_SOCIAL_DIR, RECEIPTS_DIR) as $dir) { if (!is_dir($dir)) { mkdir($dir, 0755, true); } } function e($value) { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); } function money($value) { return 'S/ ' . number_format((float)$value, 2); } function now_text() { return date('Y-m-d H:i:s'); } function url($route = 'inicio', $params = array()) { if ($route && $route !== 'inicio') { $params = array('r' => $route) + $params; } return 'index.php' . ($params ? '?' . http_build_query($params) : ''); } function redirect_to($route = 'inicio', $params = array()) { header('Location: ' . url($route, $params)); exit; } function csrf_token() { if (empty($_SESSION['csrf'])) { $_SESSION['csrf'] = bin2hex(random_bytes(24)); } return $_SESSION['csrf']; } function csrf_field() { return ''; } function check_csrf() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { return; } $token = isset($_POST['csrf']) ? $_POST['csrf'] : ''; if (!$token || empty($_SESSION['csrf']) || !hash_equals($_SESSION['csrf'], $token)) { http_response_code(403); echo 'Solicitud no valida. Vuelve atras y recarga la pagina.'; exit; } } function current_route() { if (!empty($_GET['r'])) { return trim($_GET['r'], '/'); } $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $base = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); if ($base !== '/' && strpos($path, $base) === 0) { $path = substr($path, strlen($base)); } $path = trim($path, '/'); if ($path === '' || $path === 'index.php') { return 'inicio'; } $map = array( 'login/google' => 'google-login', 'login/google/callback' => 'google-callback', 'admin/pedidos' => 'admin-pedidos', 'admin/productos' => 'admin-productos', 'admin/clientes' => 'admin-clientes', 'admin/promociones' => 'admin-promociones', 'admin/redes' => 'admin-redes', 'admin/backup' => 'admin-backup', 'admin/boleta' => 'admin-boleta' ); return isset($map[$path]) ? $map[$path] : $path; } function db() { static $pdo = null; if ($pdo) { return $pdo; } if (!extension_loaded('pdo_sqlite')) { http_response_code(500); echo '

Falta activar SQLite en PHP

'; echo '

Activa la extension pdo_sqlite en XAMPP o en tu hosting cPanel.

'; exit; } $pdo = new PDO('sqlite:' . DB_PATH); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); init_db($pdo); return $pdo; } function run_sql($sql, $params = array()) { $stmt = db()->prepare($sql); $stmt->execute($params); return $stmt; } function one($sql, $params = array()) { $stmt = run_sql($sql, $params); $row = $stmt->fetch(); return $row ? $row : null; } function all_rows($sql, $params = array()) { $stmt = run_sql($sql, $params); return $stmt->fetchAll(); } function init_db($pdo) { static $done = false; if ($done) { return; } $done = true; $pdo->exec("CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'client', google_id TEXT, auth_provider TEXT NOT NULL DEFAULT 'email', promo_opt_in INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT NOT NULL, price REAL NOT NULL, promo_price REAL, stock INTEGER NOT NULL DEFAULT 0, sizes TEXT, description TEXT, image TEXT, active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS cart_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, product_id INTEGER NOT NULL, size TEXT, qty INTEGER NOT NULL DEFAULT 1, updated_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, customer_name TEXT NOT NULL, email TEXT NOT NULL, phone TEXT NOT NULL, address TEXT NOT NULL, shipping_agency TEXT NOT NULL DEFAULT 'SHALOM', payment_method TEXT NOT NULL DEFAULT 'Yape', yape_phone TEXT NOT NULL, yape_recipient TEXT NOT NULL, yape_name TEXT, yape_operation TEXT, receipt_file TEXT, total REAL NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'Pendiente', payment_status TEXT NOT NULL DEFAULT 'En revision', admin_hidden INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS order_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id INTEGER, product_name TEXT NOT NULL, price REAL NOT NULL, quantity INTEGER NOT NULL, size TEXT, image TEXT )"); $pdo->exec("CREATE TABLE IF NOT EXISTS promotions ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, message TEXT NOT NULL, discount TEXT, recipients INTEGER NOT NULL DEFAULT 0, sent_at TEXT, created_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS social_links ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, url TEXT NOT NULL, icon TEXT, active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL )"); $pdo->exec("CREATE TABLE IF NOT EXISTS admin_settings ( setting_key TEXT PRIMARY KEY, setting_value TEXT )"); $admin = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $admin->execute(array(strtolower(ADMIN_EMAIL))); $adminRow = $admin->fetch(); if (!$adminRow) { $stmt = $pdo->prepare("INSERT INTO users (name, email, password_hash, role, promo_opt_in, created_at) VALUES (?, ?, ?, 'admin', 0, ?)"); $stmt->execute(array('Administrador JAMA', strtolower(ADMIN_EMAIL), password_hash(ADMIN_PASSWORD, PASSWORD_DEFAULT), now_text())); } elseif (RESET_ADMIN_PASSWORD) { $stmt = $pdo->prepare("UPDATE users SET password_hash = ?, role = 'admin' WHERE email = ?"); $stmt->execute(array(password_hash(ADMIN_PASSWORD, PASSWORD_DEFAULT), strtolower(ADMIN_EMAIL))); } $count = $pdo->query("SELECT COUNT(*) AS total FROM products")->fetch(); if ((int)$count['total'] === 0) { $products = array( array('Polo JAMA Signature', 'Polos', 59, 49, 18, 'S,M,L,XL', 'Polo urbano de algodon, corte comodo y detalle JAMA al frente.'), array('Jogger JAMA Street', 'Pantalones', 89, 79, 10, 'S,M,L,XL', 'Jogger urbano para diario, suave y con estilo callejero.'), array('Gorra JAMA Urban', 'Accesorios', 39, null, 25, 'Unica', 'Gorra moderna para completar el outfit JAMA.'), array('Perfume JAMA Noir', 'Perfumes', 89, 79, 15, '50ml,100ml', 'Aroma elegante, intenso y urbano. Ideal para noche y salidas.'), array('Perfume JAMA Fresh', 'Perfumes', 75, null, 20, '50ml,100ml,Unisex', 'Fragancia fresca y limpia para uso diario.'), array('Perfume JAMA Gold', 'Perfumes', 99, 89, 10, '100ml,Unisex', 'Fragancia premium dulce con fondo amaderado.') ); $stmt = $pdo->prepare("INSERT INTO products (name, category, price, promo_price, stock, sizes, description, image, active, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, '', 1, ?)"); foreach ($products as $p) { $stmt->execute(array($p[0], $p[1], $p[2], $p[3], $p[4], $p[5], $p[6], now_text())); } } } function admin_setting($key, $default = '') { $row = one("SELECT setting_value FROM admin_settings WHERE setting_key = ?", array($key)); return $row ? $row['setting_value'] : $default; } function set_admin_setting($key, $value) { run_sql("INSERT OR REPLACE INTO admin_settings (setting_key, setting_value) VALUES (?, ?)", array($key, $value)); } function current_user() { if (empty($_SESSION['user_id'])) { return null; } return one("SELECT * FROM users WHERE id = ?", array((int)$_SESSION['user_id'])); } function is_admin() { $user = current_user(); return $user && $user['role'] === 'admin'; } function require_user() { if (!current_user()) { redirect_to('login', array('err' => 'Inicia sesion para continuar.')); } } function require_admin() { if (!is_admin()) { redirect_to('admin-login', array('err' => 'Ingresa como administrador.')); } } function login_user($user) { $_SESSION['user_id'] = (int)$user['id']; merge_session_cart_to_user((int)$user['id']); } function cart_key($productId, $size) { return (int)$productId . '|' . trim((string)$size); } function parse_cart_key($key) { $parts = explode('|', $key, 2); return array((int)$parts[0], isset($parts[1]) ? $parts[1] : ''); } function session_cart() { return isset($_SESSION['cart']) && is_array($_SESSION['cart']) ? $_SESSION['cart'] : array(); } function save_session_cart($cart) { $_SESSION['cart'] = $cart; } function user_cart($userId) { $cart = array(); $rows = all_rows("SELECT product_id, size, qty FROM cart_items WHERE user_id = ?", array($userId)); foreach ($rows as $row) { $cart[cart_key($row['product_id'], $row['size'])] = (int)$row['qty']; } return $cart; } function save_user_cart($userId, $cart) { run_sql("DELETE FROM cart_items WHERE user_id = ?", array($userId)); foreach ($cart as $key => $qty) { list($productId, $size) = parse_cart_key($key); if ($productId > 0 && $qty > 0) { run_sql("INSERT INTO cart_items (user_id, product_id, size, qty, updated_at) VALUES (?, ?, ?, ?, ?)", array($userId, $productId, $size, (int)$qty, now_text())); } } } function merge_session_cart_to_user($userId) { $session = session_cart(); if (!$session) { return; } $cart = user_cart($userId); foreach ($session as $key => $qty) { $cart[$key] = isset($cart[$key]) ? $cart[$key] + (int)$qty : (int)$qty; } save_user_cart($userId, $cart); save_session_cart(array()); } function get_cart() { $user = current_user(); if ($user && $user['role'] === 'client') { return user_cart((int)$user['id']); } return session_cart(); } function save_cart($cart) { $user = current_user(); if ($user && $user['role'] === 'client') { save_user_cart((int)$user['id'], $cart); } else { save_session_cart($cart); } } function cart_count() { $total = 0; foreach (get_cart() as $qty) { $total += (int)$qty; } return $total; } function product_price($product) { if ($product['promo_price'] !== null && $product['promo_price'] !== '') { return (float)$product['promo_price']; } return (float)$product['price']; } function image_tag($path, $class = '', $alt = '') { if (!$path) { return '
JAMAStore
'; } return '' . e($alt) . ''; } function product_media($product, $small = false) { if ($small) { if (!$product['image']) { return 'JAMA'; } return '' . e($product['name']) . ''; } return image_tag($product['image'], '', $product['name']); } function product_card($product) { $price = product_price($product); $old = ($product['promo_price'] !== null && $product['promo_price'] !== '') ? '' . money($product['price']) . '' : ''; $sizes = ''; foreach (array_filter(array_map('trim', explode(',', (string)$product['sizes']))) as $size) { $sizes .= '' . e($size) . ''; } return '
' . product_media($product) . '
' . e($product['category']) . '

' . e($product['name']) . '

' . $old . money($price) . '

' . e($product['description']) . '

Disponible: ' . (int)$product['stock'] . '
' . $sizes . '
Ver prenda
'; } function social_links_html() { $rows = all_rows("SELECT * FROM social_links WHERE active = 1 ORDER BY created_at DESC"); if (!$rows) { return '

redes @.

'; } $html = '
'; foreach ($rows as $row) { $icon = $row['icon'] ? '' . e($row['name']) . '' : '' . e(substr($row['name'], 0, 1)) . ''; $html .= '' . $icon . e($row['name']) . ''; } return $html . '
'; } function notice_html() { if (!empty($_GET['ok'])) { return '
' . e($_GET['ok']) . '
'; } if (!empty($_GET['err'])) { return '
' . e($_GET['err']) . '
'; } return ''; } function shipping_badge() { return 'ShalomSHALOM'; } function page($title, $content, $active = '') { $user = current_user(); $userLinks = ''; if ($user) { if ($user['role'] === 'admin') { $userLinks .= 'Panel admin'; $userLinks .= 'Salir (Administrador)'; } else { $userLinks .= 'Mis pedidos'; $userLinks .= 'Salir'; } } else { $userLinks .= 'Ingresar'; $userLinks .= 'Crear cuenta'; } $pageTitle = $title . ' - ' . APP_NAME; $seoHead = ''; if ($active === 'inicio') { $pageTitle = 'JAMA Store Perú | Ropa Urbana y Perfumes'; $seoHead = ' '; } echo ' ' . e($pageTitle) . ' ' . $seoHead . '
' . notice_html() . $content . ' '; } function admin_page($title, $content, $active = 'dashboard') { require_admin(); $links = array( 'dashboard' => array('Panel', 'admin'), 'pedidos' => array('Pedidos', 'admin-pedidos'), 'productos' => array('Productos', 'admin-productos'), 'clientes' => array('Clientes', 'admin-clientes'), 'redes' => array('Redes', 'admin-redes'), 'promos' => array('Promociones', 'admin-promociones'), 'backup' => array('Backup', 'admin-backup') ); $nav = ''; foreach ($links as $key => $item) { $nav .= '' . e($item[0]) . ''; } echo ' ' . e($title) . ' - Admin JAMA
' . notice_html() . $content . '
'; } function page_home() { $products = all_rows("SELECT * FROM products WHERE active = 1 ORDER BY created_at DESC LIMIT 6"); $cards = ''; foreach ($products as $p) { $cards .= product_card($p); } $promo = one("SELECT * FROM promotions ORDER BY created_at DESC LIMIT 1"); $promoHtml = $promo ? '
' . e($promo['discount'] ?: 'PROMO') . '
Promo activa

' . e($promo['title']) . '

' . e($promo['message']) . '

Ver promoción
' : ''; $content = '
JAMA Store 24/7

JAMA Store Perú - Ropa urbana y perfumes

Compra prendas y perfumes JAMA, paga con Yape y envia por SHALOM. Cada cliente tiene su cuenta, carrito y pedidos guardados.

JAMA Store
JAMA StoreModa, perfumes y promociones

Novedades

Moda urbana, perfumes y promociones para encontrar tu propio estilo.

' . $cards . '
' . $promoHtml . '
'; page('Inicio', $content, 'inicio'); } function page_catalog($onlyPerfumes = false) { if ($onlyPerfumes) { $rows = all_rows("SELECT * FROM products WHERE active = 1 AND category = 'Perfumes' ORDER BY created_at DESC"); $title = 'Perfumes JAMA'; $active = 'perfumes'; $desc = 'Fragancias para sumar a tu tienda con el mismo carrito.'; } else { $rows = all_rows("SELECT * FROM products WHERE active = 1 AND category != 'Perfumes' ORDER BY created_at DESC"); $title = 'Catalogo JAMA'; $active = 'catalogo'; $desc = 'Ropa, accesorios y prendas urbanas.'; } $cards = ''; foreach ($rows as $p) { $cards .= product_card($p); } if (!$cards) { $cards = '
Aun no hay productos activos.
'; } $content = '
' . e($active) . '

' . e($title) . '

' . e($desc) . '

' . $cards . '
'; page($title, $content, $active); } function page_product() { $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $product = one("SELECT * FROM products WHERE id = ? AND active = 1", array($id)); if (!$product) { page('No encontrado', '
Producto no encontrado.
'); return; } $sizes = ''; foreach (array_filter(array_map('trim', explode(',', (string)$product['sizes']))) as $size) { $sizes .= ''; } if (!$sizes) { $sizes = ''; } $old = ($product['promo_price'] !== null && $product['promo_price'] !== '') ? '' . money($product['price']) . '' : ''; $content = '
' . product_media($product) . '
' . e($product['category']) . '

' . e($product['name']) . '

' . $old . money(product_price($product)) . '

' . e($product['description']) . '

' . csrf_field() . '
'; page($product['name'], $content, $product['category'] === 'Perfumes' ? 'perfumes' : 'catalogo'); } function cart_lines() { $lines = array(); $total = 0; foreach (get_cart() as $key => $qty) { list($productId, $size) = parse_cart_key($key); $product = one("SELECT * FROM products WHERE id = ?", array($productId)); if (!$product) { continue; } $price = product_price($product); $subtotal = $price * (int)$qty; $total += $subtotal; $lines[] = array('key' => $key, 'product' => $product, 'size' => $size, 'qty' => (int)$qty, 'price' => $price, 'subtotal' => $subtotal); } return array($lines, $total); } function page_cart() { list($lines, $total) = cart_lines(); $rows = ''; foreach ($lines as $line) { $p = $line['product']; $rows .= '
' . product_media($p, true) . '
' . e($p['name']) . '
Variante: ' . e($line['size']) . '
' . money($line['price']) . '
' . csrf_field() . '
' . money($line['subtotal']) . '
' . csrf_field() . '
'; } if (!$rows) { $rows = 'Tu carrito esta vacio.'; } $user = current_user(); if ($user && $user['role'] === 'client' && $lines) { $checkout = '
' . csrf_field() . '
FinalizaPago por Yape y envio SHALOM
' . money($total) . '
DATOS DE ENTREGA¿Dónde coordinamos tu pedido?Completa estos datos para preparar y enviar tu compra correctamente.
Verifica tus datos antes de confirmar el pedido.
Shalom
YapeYapea aqui mismo al numero ' . e(YAPE_PHONE) . 'Destinatario: ' . e(YAPE_RECIPIENT) . '. Sube tu comprobante para validar el pedido.
Numero' . e(YAPE_PHONE) . '
Nombre' . e(YAPE_RECIPIENT) . '
Total' . money($total) . '
'; } elseif ($lines) { $checkout = '

Inicia sesion para comprar

Asi tu pedido queda guardado con tu correo y lo puedes ver en Mis pedidos.

Ingresar Crear cuenta
'; } else { $checkout = '

Total

Carrito' . money(0) . '
'; } $content = '
Carrito

Finaliza tu compra

' . $rows . '
PrendaPrecioCantidadSubtotal
' . $checkout . '
'; page('Carrito', $content); } function handle_add_to_cart() { check_csrf(); $productId = isset($_POST['product_id']) ? (int)$_POST['product_id'] : 0; $qty = max(1, isset($_POST['qty']) ? (int)$_POST['qty'] : 1); $size = trim(isset($_POST['size']) ? $_POST['size'] : ''); $product = one("SELECT * FROM products WHERE id = ? AND active = 1", array($productId)); if (!$product) { redirect_to('catalogo', array('err' => 'Producto no encontrado.')); } $key = cart_key($productId, $size); $cart = get_cart(); $cart[$key] = isset($cart[$key]) ? $cart[$key] + $qty : $qty; save_cart($cart); redirect_to('carrito', array('ok' => 'Prenda agregada al carrito.')); } function handle_cart_update() { check_csrf(); $key = isset($_POST['key']) ? $_POST['key'] : ''; $qty = isset($_POST['qty']) ? (int)$_POST['qty'] : 0; $cart = get_cart(); if (isset($cart[$key])) { if ($qty <= 0) { unset($cart[$key]); } else { $cart[$key] = $qty; } } save_cart($cart); redirect_to('carrito', array('ok' => 'Carrito actualizado.')); } function handle_cart_remove() { check_csrf(); $key = isset($_POST['key']) ? $_POST['key'] : ''; $cart = get_cart(); if (isset($cart[$key])) { unset($cart[$key]); save_cart($cart); } redirect_to('carrito', array('ok' => 'Prenda eliminada del carrito.')); } function relative_path($absolute) { return str_replace('\\', '/', ltrim(str_replace(BASE_PATH, '', $absolute), '/')); } function upload_file($field, $dir, $prefix, $allowed) { if (empty($_FILES[$field]) || $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) { return ''; } if ($_FILES[$field]['error'] !== UPLOAD_ERR_OK) { return ''; } $ext = strtolower(pathinfo($_FILES[$field]['name'], PATHINFO_EXTENSION)); if (!in_array($ext, $allowed, true)) { return ''; } $name = $prefix . '-' . bin2hex(random_bytes(8)) . '.' . $ext; $target = rtrim($dir, '/\\') . '/' . $name; if (!move_uploaded_file($_FILES[$field]['tmp_name'], $target)) { return ''; } return relative_path($target); } function handle_create_order() { check_csrf(); require_user(); $user = current_user(); if ($user['role'] !== 'client') { redirect_to('carrito', array('err' => 'Usa una cuenta de cliente para comprar.')); } list($lines, $total) = cart_lines(); if (!$lines) { redirect_to('carrito', array('err' => 'Tu carrito esta vacio.')); } $phone = trim(isset($_POST['phone']) ? $_POST['phone'] : ''); $address = trim(isset($_POST['address']) ? $_POST['address'] : ''); $agency = trim(isset($_POST['shipping_agency']) ? $_POST['shipping_agency'] : 'SHALOM'); $yapeName = trim(isset($_POST['yape_name']) ? $_POST['yape_name'] : ''); $operation = trim(isset($_POST['yape_operation']) ? $_POST['yape_operation'] : ''); $receipt = upload_file('receipt', RECEIPTS_DIR, 'comprobante', array('jpg', 'jpeg', 'png', 'webp', 'pdf')); if (!$phone || !$address || !$yapeName || !$receipt) { redirect_to('carrito', array('err' => 'Completa direccion, telefono, nombre Yape y comprobante.')); } run_sql("INSERT INTO orders (user_id, customer_name, email, phone, address, shipping_agency, yape_phone, yape_recipient, yape_name, yape_operation, receipt_file, total, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", array($user['id'], $user['name'], $user['email'], $phone, $address, $agency ?: 'SHALOM', YAPE_PHONE, YAPE_RECIPIENT, $yapeName, $operation, $receipt, $total, now_text())); $orderId = (int)db()->lastInsertId(); foreach ($lines as $line) { $p = $line['product']; run_sql("INSERT INTO order_items (order_id, product_id, product_name, price, quantity, size, image) VALUES (?, ?, ?, ?, ?, ?, ?)", array($orderId, $p['id'], $p['name'], $line['price'], $line['qty'], $line['size'], $p['image'])); run_sql("UPDATE products SET stock = MAX(stock - ?, 0) WHERE id = ?", array($line['qty'], $p['id'])); } save_cart(array()); redirect_to('pedido-exito', array('id' => $orderId)); } function page_order_success() { require_user(); $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $order = one("SELECT * FROM orders WHERE id = ? AND user_id = ?", array($id, current_user()['id'])); if (!$order) { redirect_to('mis-pedidos'); } $content = '
Pedido recibido

Gracias por tu compra

Tu pedido #' . (int)$order['id'] . ' ya aparece en tu cuenta y en el panel admin. El pago queda en revision hasta validar el Yape.

Ver mis pedidos
Total

' . money($order['total']) . '

' . e($order['payment_status']) . '
'; page('Pedido recibido', $content); } function page_my_orders() { require_user(); $user = current_user(); $orders = all_rows("SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC", array($user['id'])); $rows = ''; foreach ($orders as $o) { $rows .= '#' . (int)$o['id'] . '' . e($o['created_at']) . '' . money($o['total']) . '' . e($o['status']) . '' . e($o['payment_status']) . 'Ver'; } if (!$rows) { $rows = 'Aun no hiciste pedidos.'; } $content = '
Mi cuenta

Pedidos realizados

' . $rows . '
PedidoFechaTotalEnvioPago
'; page('Mis pedidos', $content); } function page_customer_order() { require_user(); $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $user = current_user(); $order = one("SELECT * FROM orders WHERE id = ? AND user_id = ?", array($id, $user['id'])); if (!$order) { redirect_to('mis-pedidos'); } $items = all_rows("SELECT * FROM order_items WHERE order_id = ?", array($id)); $rows = ''; foreach ($items as $it) { $variant = trim((string)$it['size']) !== '' ? e($it['size']) : 'Única'; $rows .= '
' . image_tag($it['image'], 'mini-img', $it['product_name']) . '
' . e($it['product_name']) . 'Variante: ' . $variant . '
' . (int)$it['quantity'] . ' ' . money($it['price']) . ' ' . money($it['price'] * $it['quantity']) . ' '; } if (!$rows) { $rows = 'Este pedido no tiene productos.'; } $operationText = trim((string)$order['yape_operation']) !== '' ? e($order['yape_operation']) : 'No registrado'; $yapeNameText = trim((string)$order['yape_name']) !== '' ? e($order['yape_name']) : 'No registrado'; $content = '
Pedido #' . (int)$order['id'] . '

Detalle del pedido

' . e($order['status']) . ' ' . e($order['payment_status']) . '
TU COMPRAProductos del pedido
' . count($items) . ' producto(s)
' . $rows . '
ProductoCant.PrecioSubtotal
'; page('Pedido #' . $id, $content); } function google_configured() { return GOOGLE_CLIENT_ID !== '' && GOOGLE_CLIENT_SECRET !== ''; } function app_url() { if (APP_URL !== '') { return rtrim(APP_URL, '/'); } $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443); $scheme = $https ? 'https' : 'http'; $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; $base = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); return rtrim($scheme . '://' . $host . ($base === '/' ? '' : $base), '/'); } function google_redirect_uri() { return GOOGLE_REDIRECT_URI !== '' ? GOOGLE_REDIRECT_URI : app_url() . '/index.php?r=google-callback'; } function google_button() { if (!google_configured()) { return '
Google Login esta listo, pero falta poner GOOGLE_CLIENT_ID y GOOGLE_CLIENT_SECRET en config.php.
'; } return 'G Continuar con Google'; } function page_login($admin = false) { $action = $admin ? 'admin-login' : 'login'; $extra = $admin ? '' : '
o
' . google_button() . '

No tienes cuenta? Crea una aqui

'; $content = '
' . csrf_field() . '

' . ($admin ? 'Panel admin' : 'Ingresar') . '

Accede con tu correo.

' . $extra . '
'; page($admin ? 'Admin' : 'Ingresar', $content); } function page_register() { $content = '
' . csrf_field() . '

Crear cuenta

Tu carrito y pedidos quedaran guardados con tu correo.

o
' . google_button() . '

Ya tienes cuenta? Ingresa aqui

'; page('Registro', $content); } function handle_login($admin = false) { check_csrf(); $email = strtolower(trim(isset($_POST['email']) ? $_POST['email'] : '')); $password = isset($_POST['password']) ? $_POST['password'] : ''; $user = one("SELECT * FROM users WHERE email = ?", array($email)); if (!$user || !password_verify($password, $user['password_hash'])) { redirect_to($admin ? 'admin-login' : 'login', array('err' => 'Correo o contrasena incorrectos.')); } if ($admin && $user['role'] !== 'admin') { redirect_to('admin-login', array('err' => 'Esta cuenta no es admin.')); } login_user($user); redirect_to($user['role'] === 'admin' ? 'admin' : 'inicio', array('ok' => 'Sesion iniciada.')); } function handle_register() { check_csrf(); $name = trim(isset($_POST['name']) ? $_POST['name'] : ''); $email = strtolower(trim(isset($_POST['email']) ? $_POST['email'] : '')); $password = isset($_POST['password']) ? $_POST['password'] : ''; if (!$name || !$email || strlen($password) < 4) { redirect_to('registro', array('err' => 'Completa tus datos.')); } try { run_sql("INSERT INTO users (name, email, password_hash, role, promo_opt_in, created_at) VALUES (?, ?, ?, 'client', ?, ?)", array($name, $email, password_hash($password, PASSWORD_DEFAULT), isset($_POST['promo_opt_in']) ? 1 : 0, now_text())); } catch (Exception $e) { redirect_to('registro', array('err' => 'Ese correo ya esta registrado.')); } $user = one("SELECT * FROM users WHERE email = ?", array($email)); login_user($user); redirect_to('inicio', array('ok' => 'Cuenta creada.')); } function handle_logout() { session_destroy(); redirect_to('inicio', array('ok' => 'Sesion cerrada.')); } function handle_google_login() { if (!google_configured()) { redirect_to('login', array('err' => 'Falta configurar Google Login en config.php.')); } $_SESSION['google_state'] = bin2hex(random_bytes(16)); $params = array( 'client_id' => GOOGLE_CLIENT_ID, 'redirect_uri' => google_redirect_uri(), 'response_type' => 'code', 'scope' => 'openid email profile', 'state' => $_SESSION['google_state'], 'access_type' => 'online', 'prompt' => 'select_account' ); header('Location: https://accounts.google.com/o/oauth2/v2/auth?' . http_build_query($params)); exit; } function curl_json($url, $postFields = null, $bearer = '') { if (!function_exists('curl_init')) { return null; } $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); if ($postFields !== null) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postFields)); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded')); } elseif ($bearer) { curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . $bearer)); } $body = curl_exec($ch); curl_close($ch); return $body ? json_decode($body, true) : null; } function handle_google_callback() { if (!google_configured()) { redirect_to('login', array('err' => 'Google Login no esta configurado.')); } if (!empty($_GET['error'])) { redirect_to('login', array('err' => 'Google cancelo el acceso.')); } $state = isset($_GET['state']) ? $_GET['state'] : ''; if (!$state || empty($_SESSION['google_state']) || !hash_equals($_SESSION['google_state'], $state)) { redirect_to('login', array('err' => 'Estado de Google no valido.')); } $code = isset($_GET['code']) ? $_GET['code'] : ''; $token = curl_json('https://oauth2.googleapis.com/token', array( 'code' => $code, 'client_id' => GOOGLE_CLIENT_ID, 'client_secret' => GOOGLE_CLIENT_SECRET, 'redirect_uri' => google_redirect_uri(), 'grant_type' => 'authorization_code' )); if (!$token || empty($token['access_token'])) { redirect_to('login', array('err' => 'No se pudo conectar con Google.')); } $info = curl_json('https://www.googleapis.com/oauth2/v3/userinfo', null, $token['access_token']); if (!$info || empty($info['email'])) { redirect_to('login', array('err' => 'Google no devolvio el correo.')); } $email = strtolower($info['email']); $googleId = isset($info['sub']) ? $info['sub'] : ''; $name = isset($info['name']) ? $info['name'] : $email; $user = one("SELECT * FROM users WHERE google_id = ? AND google_id != ''", array($googleId)); if (!$user) { $user = one("SELECT * FROM users WHERE email = ?", array($email)); if ($user) { run_sql("UPDATE users SET google_id = ?, auth_provider = 'google' WHERE id = ?", array($googleId, $user['id'])); } else { run_sql("INSERT INTO users (name, email, password_hash, role, google_id, auth_provider, promo_opt_in, created_at) VALUES (?, ?, ?, 'client', ?, 'google', 1, ?)", array($name, $email, password_hash(bin2hex(random_bytes(18)), PASSWORD_DEFAULT), $googleId, now_text())); } $user = one("SELECT * FROM users WHERE email = ?", array($email)); } login_user($user); redirect_to('inicio', array('ok' => 'Entraste con Google.')); } function admin_dashboard() { $orders = one("SELECT COUNT(*) AS total FROM orders"); $clients = one("SELECT COUNT(*) AS total FROM users WHERE role = 'client'"); $products = one("SELECT COUNT(*) AS total FROM products WHERE active = 1"); $pending = one("SELECT COUNT(*) AS total FROM orders WHERE payment_status != 'Aprobado' AND status != 'Cancelado'"); $resetAt = admin_setting('earnings_reset_at', ''); $sql = "SELECT COALESCE(SUM(total),0) AS total FROM orders WHERE payment_status = 'Aprobado' AND status != 'Cancelado'"; $params = array(); if ($resetAt !== '') { $sql .= " AND created_at >= ?"; $params[] = $resetAt; } $earnings = one($sql, $params); $resetLabel = $resetAt !== '' ? 'Desde: ' . $resetAt : 'Desde el inicio'; $content = '
Admin

Panel JAMA

Ver tienda
Pedidos' . (int)$orders['total'] . '
Ganancia' . money($earnings['total']) . '' . e($resetLabel) . '
Clientes' . (int)$clients['total'] . '
Pagos pendientes' . (int)$pending['total'] . '

Acciones rapidas

Revisa pagos, pedidos, stock, promociones y redes.

Ver pedidos Productos

Ganancia del panel

Este contador suma pedidos con pago aprobado. Reiniciarlo no borra pedidos ni ventas guardadas.

Acumulado' . money($earnings['total']) . '
' . csrf_field() . '
'; admin_page('Panel', $content, 'dashboard'); } function handle_admin_earnings_reset() { check_csrf(); require_admin(); set_admin_setting('earnings_reset_at', now_text()); redirect_to('admin', array('ok' => 'Ganancia reiniciada a S/ 0.00. Tus pedidos siguen guardados.')); } function admin_order_items_summary($orderId) { $items = all_rows("SELECT * FROM order_items WHERE order_id = ?", array($orderId)); $html = ''; foreach ($items as $it) { $html .= '
' . image_tag($it['image'], 'mini-img tiny', $it['product_name']) . '
' . e($it['product_name']) . '
' . e($it['size']) . ' x ' . (int)$it['quantity'] . '
'; } return $html; } function admin_orders() { $orders = all_rows("SELECT * FROM orders WHERE admin_hidden = 0 ORDER BY created_at DESC"); $rows = ''; foreach ($orders as $o) { $remove = ''; if (in_array($o['status'], array('Entregado','Cancelado'), true)) { $remove = '
' . csrf_field() . '
'; } $rows .= '#' . (int)$o['id'] . '
' . e($o['created_at']) . '' . e($o['customer_name']) . '
' . e($o['email']) . '' . admin_order_items_summary($o['id']) . '' . e($o['address']) . '
' . shipping_badge() . '' . money($o['total']) . '
' . e($o['payment_status']) . '' . e($o['status']) . 'Ver ' . $remove . ''; } if (!$rows) { $rows = 'No hay pedidos visibles.'; } $content = '

Pedidos

' . $rows . '
PedidoClientePrendasEnvioTotal/PagoEstado
'; admin_page('Pedidos', $content, 'pedidos'); } function admin_order_detail() { $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $order = one("SELECT * FROM orders WHERE id = ?", array($id)); if (!$order) { redirect_to('admin-pedidos', array('err' => 'Pedido no encontrado.')); } $items = all_rows("SELECT * FROM order_items WHERE order_id = ?", array($id)); $rows = ''; foreach ($items as $it) { $rows .= '
' . image_tag($it['image'], 'mini-img', $it['product_name']) . '
' . e($it['product_name']) . '
Talla/variante: ' . e($it['size']) . '
' . (int)$it['quantity'] . '' . money($it['price']) . '' . money($it['price'] * $it['quantity']) . ''; } $receipt = $order['receipt_file'] ? 'Ver comprobante Yape' : 'Sin comprobante'; $invoice = $order['payment_status'] === 'Aprobado' ? 'Generar boleta PDF' : 'Aprueba el pago para generar boleta.'; $delete = in_array($order['status'], array('Entregado', 'Cancelado'), true) ? '
' . csrf_field() . '
' : ''; $content = '
Pedido #' . (int)$order['id'] . '

Detalle del pedido

Volver
' . $rows . '
PrendaCant.PrecioSubtotal

Cliente y envio

' . e($order['customer_name']) . '
' . e($order['email']) . '
' . e($order['phone']) . '

Direccion:
' . e($order['address']) . '

Agencia:
' . shipping_badge() . '

Yape

Numero: ' . e($order['yape_phone']) . '
Destinatario: ' . e($order['yape_recipient']) . '
Nombre Yape: ' . e($order['yape_name']) . '
Operacion: ' . e($order['yape_operation']) . '

' . $receipt . '

Boleta

' . $invoice . '
' . csrf_field() . '
' . $delete . '
Total' . money($order['total']) . '
'; admin_page('Pedido #' . $id, $content, 'pedidos'); } function options($values, $selected) { $html = ''; foreach ($values as $value) { $html .= ''; } return $html; } function handle_admin_order_status() { check_csrf(); require_admin(); $id = (int)$_POST['id']; $status = trim($_POST['status']); $payment = trim($_POST['payment_status']); run_sql("UPDATE orders SET status = ?, payment_status = ? WHERE id = ?", array($status, $payment, $id)); redirect_to('admin-pedido', array('id' => $id, 'ok' => 'Pedido actualizado.')); } function handle_admin_order_delete() { check_csrf(); require_admin(); $id = (int)$_POST['id']; $order = one("SELECT * FROM orders WHERE id = ?", array($id)); if (!$order || !in_array($order['status'], array('Entregado', 'Cancelado'), true)) { redirect_to('admin-pedidos', array('err' => 'Solo puedes quitar pedidos entregados o cancelados.')); } run_sql("UPDATE orders SET admin_hidden = 1 WHERE id = ?", array($id)); redirect_to('admin-pedidos', array('ok' => 'Pedido quitado del panel. El cliente aun lo conserva en su cuenta.')); } function admin_products() { $rows = all_rows("SELECT * FROM products ORDER BY active DESC, created_at DESC"); $html = ''; foreach ($rows as $p) { $action = $p['active'] ? '
' . csrf_field() . '
' : '
' . csrf_field() . '
'; $html .= '' . product_media($p, true) . '' . e($p['name']) . '
' . e($p['category']) . '' . money($p['price']) . '' . ($p['promo_price'] ? money($p['promo_price']) : '-') . '' . (int)$p['stock'] . '' . e($p['sizes']) . 'Editar' . $action . ''; } if (!$html) { $html = 'No hay productos.'; } $content = '

Productos y stock

Agregar prenda Agregar hasta 20
' . $html . '
FotoProductoPrecioPromoStockTallas
'; admin_page('Productos', $content, 'productos'); } function admin_product_form() { $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $p = $id ? one("SELECT * FROM products WHERE id = ?", array($id)) : null; $action = $p ? 'admin-producto-guardar' : 'admin-producto-crear'; $image = $p && $p['image'] ? '
Foto actual
' : ''; $content = '

' . ($p ? 'Editar producto' : 'Agregar producto') . '

Volver
' . csrf_field() . ' ' . $image . '
'; admin_page('Producto', $content, 'productos'); } function admin_products_bulk_form() { require_admin(); $categories = array('Polos', 'Camisas', 'Poleras', 'Pantalones', 'Shorts', 'Accesorios', 'Zapatillas', 'Perfumes', 'Otros'); $rows = ''; for ($i = 0; $i < 20; $i++) { $number = $i + 1; $options = ''; foreach ($categories as $category) { $options .= ''; } $rows .= '

Producto ' . $number . '

'; } $content = '

Agregar hasta 20 productos

Completa solo los productos que quieras agregar. Los espacios vacios se ignoran. Usa la categoria Perfumes para que aparezcan en la seccion Perfumes; las demas categorias aparecen en Catalogo JAMA.

Volver
' . csrf_field() . ' ' . $rows . '
'; admin_page('Agregar productos', $content, 'productos'); } function upload_file_array($field, $index, $dir, $prefix, $allowed) { if (empty($_FILES[$field]) || !isset($_FILES[$field]['error'][$index]) || $_FILES[$field]['error'][$index] === UPLOAD_ERR_NO_FILE) { return ''; } if ($_FILES[$field]['error'][$index] !== UPLOAD_ERR_OK) { return ''; } $originalName = isset($_FILES[$field]['name'][$index]) ? $_FILES[$field]['name'][$index] : ''; $tmpName = isset($_FILES[$field]['tmp_name'][$index]) ? $_FILES[$field]['tmp_name'][$index] : ''; $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION)); if (!$tmpName || !in_array($ext, $allowed, true)) { return ''; } $name = $prefix . '-' . bin2hex(random_bytes(8)) . '.' . $ext; $target = rtrim($dir, '/\\') . '/' . $name; if (!move_uploaded_file($tmpName, $target)) { return ''; } return relative_path($target); } function handle_products_bulk_save() { check_csrf(); require_admin(); $names = isset($_POST['name']) && is_array($_POST['name']) ? $_POST['name'] : array(); $categories = isset($_POST['category']) && is_array($_POST['category']) ? $_POST['category'] : array(); $prices = isset($_POST['price']) && is_array($_POST['price']) ? $_POST['price'] : array(); $promoPrices = isset($_POST['promo_price']) && is_array($_POST['promo_price']) ? $_POST['promo_price'] : array(); $stocks = isset($_POST['stock']) && is_array($_POST['stock']) ? $_POST['stock'] : array(); $sizes = isset($_POST['sizes']) && is_array($_POST['sizes']) ? $_POST['sizes'] : array(); $descriptions = isset($_POST['description']) && is_array($_POST['description']) ? $_POST['description'] : array(); $created = 0; $skipped = 0; $max = min(20, count($names)); for ($i = 0; $i < $max; $i++) { $name = trim(isset($names[$i]) ? $names[$i] : ''); $priceText = trim(isset($prices[$i]) ? (string)$prices[$i] : ''); if ($name === '') { continue; } if ($priceText === '' || !is_numeric($priceText)) { $skipped++; continue; } $category = trim(isset($categories[$i]) ? $categories[$i] : 'Polos'); if ($category === '') { $category = 'Polos'; } $price = max(0, (float)$priceText); $promoText = trim(isset($promoPrices[$i]) ? (string)$promoPrices[$i] : ''); $promoPrice = ($promoText === '' || !is_numeric($promoText)) ? null : max(0, (float)$promoText); $stock = max(0, (int)(isset($stocks[$i]) ? $stocks[$i] : 0)); $sizeText = trim(isset($sizes[$i]) ? $sizes[$i] : ''); $description = trim(isset($descriptions[$i]) ? $descriptions[$i] : ''); $image = upload_file_array('images', $i, UPLOAD_PRODUCTS_DIR, 'producto', array('jpg','jpeg','png','webp')); run_sql( "INSERT INTO products (name, category, price, promo_price, stock, sizes, description, image, active, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?)", array($name, $category, $price, $promoPrice, $stock, $sizeText, $description, $image, now_text()) ); $created++; } if ($created === 0) { redirect_to('admin-productos-masivo', array('err' => 'No se agregaron productos. Completa al menos nombre y precio.')); } $message = $created . ' producto(s) agregado(s) correctamente.'; if ($skipped > 0) { $message .= ' ' . $skipped . ' fila(s) se omitieron por no tener precio valido.'; } redirect_to('admin-productos', array('ok' => $message)); } function handle_product_save($create = false) { check_csrf(); require_admin(); $id = isset($_POST['id']) ? (int)$_POST['id'] : 0; $current = $id ? one("SELECT * FROM products WHERE id = ?", array($id)) : null; $image = upload_file('image', UPLOAD_PRODUCTS_DIR, 'producto', array('jpg','jpeg','png','webp')); if (!$image && $current) { $image = $current['image']; } $data = array( trim($_POST['name']), trim($_POST['category']), (float)$_POST['price'], $_POST['promo_price'] === '' ? null : (float)$_POST['promo_price'], (int)$_POST['stock'], trim($_POST['sizes']), trim($_POST['description']), $image ); if ($create) { run_sql("INSERT INTO products (name, category, price, promo_price, stock, sizes, description, image, active, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?)", array_merge($data, array(now_text()))); } else { $data[] = $id; run_sql("UPDATE products SET name = ?, category = ?, price = ?, promo_price = ?, stock = ?, sizes = ?, description = ?, image = ? WHERE id = ?", $data); } redirect_to('admin-productos', array('ok' => 'Producto guardado.')); } function handle_product_active($active) { check_csrf(); require_admin(); $id = (int)$_POST['id']; run_sql("UPDATE products SET active = ? WHERE id = ?", array($active ? 1 : 0, $id)); redirect_to('admin-productos', array('ok' => $active ? 'Producto restaurado.' : 'Producto ocultado.')); } function admin_clients() { $rows = all_rows("SELECT u.*, COUNT(o.id) AS orders_count, COALESCE(SUM(o.total),0) AS spent FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE u.role = 'client' GROUP BY u.id ORDER BY u.created_at DESC"); $html = ''; foreach ($rows as $u) { $html .= '' . e($u['name']) . '
' . e($u['email']) . '' . e($u['auth_provider']) . '' . (int)$u['orders_count'] . '' . money($u['spent']) . '' . ($u['promo_opt_in'] ? 'Si' : 'No') . ''; } if (!$html) { $html = 'Aun no hay clientes.'; } $content = '

Clientes

' . $html . '
ClienteRegistroPedidosTotalPromos
'; admin_page('Clientes', $content, 'clientes'); } function admin_promotions() { $recipientCount = one("SELECT COUNT(*) AS total FROM users WHERE role = 'client' AND promo_opt_in = 1"); $promos = all_rows("SELECT * FROM promotions ORDER BY created_at DESC"); $history = ''; foreach ($promos as $p) { $discount = trim((string)$p['discount']) !== '' ? $p['discount'] : 'PROMO'; $history .= '
' . e($discount) . '
' . e($p['title']) . '

' . e($p['message']) . '

' . (int)$p['recipients'] . ' clientes' . e($p['created_at']) . '
'; } if (!$history) { $history = '
Aun no hay promociones.
'; } $content = '
Marketing

Promociones

' . csrf_field() . '
Nueva promoción

Crea una promo clara y llamativa

El descuento se mostrará como el elemento principal para tus clientes.

Clientes que aceptan promociones' . (int)$recipientCount['total'] . '
Si tu hosting tiene correo activo, también se intentará enviar por email.
Historial

Promociones publicadas

Vista rápida del descuento, alcance y fecha.

' . $history . '
'; admin_page('Promociones', $content, 'promos'); } function handle_promotion_create() { check_csrf(); require_admin(); $title = trim($_POST['title']); $message = trim($_POST['message']); $discount = trim(isset($_POST['discount']) ? $_POST['discount'] : ''); $clients = all_rows("SELECT email FROM users WHERE role = 'client' AND promo_opt_in = 1"); foreach ($clients as $client) { @mail($client['email'], $title, $message, 'From: ' . CONTACT_EMAIL); } run_sql("INSERT INTO promotions (title, message, discount, recipients, sent_at, created_at) VALUES (?, ?, ?, ?, ?, ?)", array($title, $message, $discount, count($clients), now_text(), now_text())); redirect_to('admin-promociones', array('ok' => 'Promocion guardada.')); } function page_promos() { $rows = all_rows("SELECT * FROM promotions ORDER BY created_at DESC LIMIT 12"); $html = ''; foreach ($rows as $p) { $discount = trim((string)$p['discount']) !== '' ? $p['discount'] : 'PROMO'; $html .= '
Oferta JAMA
' . e($discount) . '

' . e($p['title']) . '

' . e($p['message']) . '

Disponible en JAMA StoreVer catálogo
'; } if (!$html) { $html = '
Aun no hay promociones publicadas.
'; } $content = '
Promos

Promociones JAMA

Descuentos y oportunidades activas para comprar tus prendas y productos favoritos.

' . $html . '
'; page('Promos', $content, 'promos'); } function admin_socials() { $rows = all_rows("SELECT * FROM social_links ORDER BY active DESC, created_at DESC"); $html = ''; foreach ($rows as $s) { $icon = $s['icon'] ? '' : '' . e(substr($s['name'], 0, 1)) . ''; $html .= '' . $icon . '' . e($s['name']) . '
' . e($s['url']) . '
' . csrf_field() . '
'; } if (!$html) { $html = 'Aun no agregaste redes.'; } $content = '

Redes sociales

' . csrf_field() . '
' . $html . '
IconoRed
'; admin_page('Redes', $content, 'redes'); } function normalize_social_url($url) { $url = trim($url); if ($url && !preg_match('/^https?:\/\//i', $url)) { $url = 'https://' . $url; } return $url; } function handle_social_create() { check_csrf(); require_admin(); $icon = upload_file('icon', UPLOAD_SOCIAL_DIR, 'red', array('jpg','jpeg','png','webp')); run_sql("INSERT INTO social_links (name, url, icon, active, created_at) VALUES (?, ?, ?, 1, ?)", array(trim($_POST['name']), normalize_social_url($_POST['url']), $icon, now_text())); redirect_to('admin-redes', array('ok' => 'Red social agregada.')); } function handle_social_delete() { check_csrf(); require_admin(); run_sql("DELETE FROM social_links WHERE id = ?", array((int)$_POST['id'])); redirect_to('admin-redes', array('ok' => 'Red social eliminada.')); } function serve_receipt() { require_user(); $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $order = one("SELECT * FROM orders WHERE id = ?", array($id)); $user = current_user(); if (!$order || (!$user || ($user['role'] !== 'admin' && (int)$order['user_id'] !== (int)$user['id']))) { http_response_code(403); echo 'No autorizado.'; exit; } $file = BASE_PATH . '/' . $order['receipt_file']; if (!is_file($file)) { http_response_code(404); echo 'Comprobante no encontrado.'; exit; } $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); $types = array('jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'webp' => 'image/webp', 'pdf' => 'application/pdf'); header('Content-Type: ' . (isset($types[$ext]) ? $types[$ext] : 'application/octet-stream')); header('Content-Length: ' . filesize($file)); readfile($file); exit; } function pdf_escape($text) { $text = (string)$text; if (function_exists('iconv')) { $converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text); if ($converted !== false) { $text = $converted; } } return str_replace(array('\\', '(', ')', "\r", "\n"), array('\\\\', '\\(', '\\)', ' ', ' '), $text); } function pdf_short($text, $max = 36) { $text = trim((string)$text); if (strlen($text) <= $max) { return $text; } return substr($text, 0, max(0, $max - 3)) . '...'; } function pdf_text_cmd($x, $y, $size, $text, $font = 'F1', $rgb = array(0.12,0.12,0.14)) { return sprintf("BT /%s %.2f Tf %.3f %.3f %.3f rg %.2f %.2f Td (%s) Tj ET\n", $font, $size, $rgb[0], $rgb[1], $rgb[2], $x, $y, pdf_escape($text)); } function jama_invoice_pdf($order, $items) { $c = ''; // Fondo y cabecera premium. $c .= "0.075 0.075 0.090 rg 0 760 595 82 re f\n"; $c .= "0.780 0.050 0.080 rg 0 754 595 6 re f\n"; $c .= pdf_text_cmd(38, 805, 24, 'JAMA STORE', 'F2', array(1,1,1)); $c .= pdf_text_cmd(39, 784, 10, 'Ropa urbana y perfumes | Peru', 'F1', array(0.82,0.82,0.84)); $c .= pdf_text_cmd(420, 806, 12, 'COMPROBANTE', 'F2', array(1,1,1)); $c .= pdf_text_cmd(420, 787, 10, 'Pedido #' . str_pad($order['id'], 6, '0', STR_PAD_LEFT), 'F1', array(0.88,0.88,0.90)); // Datos generales. $c .= "0.965 0.965 0.972 rg 32 650 531 82 re f\n"; $c .= pdf_text_cmd(46, 710, 9, 'CLIENTE', 'F2', array(0.45,0.45,0.48)); $c .= pdf_text_cmd(46, 692, 12, pdf_short($order['customer_name'], 42), 'F2'); $c .= pdf_text_cmd(46, 675, 9, pdf_short($order['email'], 52), 'F1', array(0.32,0.32,0.35)); $c .= pdf_text_cmd(310, 710, 9, 'FECHA / PAGO', 'F2', array(0.45,0.45,0.48)); $c .= pdf_text_cmd(310, 692, 10, $order['created_at'], 'F1'); $c .= pdf_text_cmd(310, 675, 9, 'Yape: ' . pdf_short($order['yape_name'], 28), 'F1', array(0.32,0.32,0.35)); $c .= pdf_text_cmd(46, 635, 9, 'ENVIO', 'F2', array(0.45,0.45,0.48)); $c .= pdf_text_cmd(46, 619, 9, pdf_short($order['address'], 74), 'F1'); $c .= pdf_text_cmd(46, 603, 9, 'Agencia: ' . $order['shipping_agency'] . ' | Telefono: ' . $order['phone'], 'F1', array(0.32,0.32,0.35)); // Tabla. $tableTop = 565; $c .= "0.075 0.075 0.090 rg 32 " . ($tableTop - 4) . " 531 28 re f\n"; $c .= pdf_text_cmd(42, $tableTop + 5, 9, 'PRODUCTO', 'F2', array(1,1,1)); $c .= pdf_text_cmd(305, $tableTop + 5, 9, 'VARIANTE', 'F2', array(1,1,1)); $c .= pdf_text_cmd(400, $tableTop + 5, 9, 'CANT.', 'F2', array(1,1,1)); $c .= pdf_text_cmd(450, $tableTop + 5, 9, 'PRECIO', 'F2', array(1,1,1)); $c .= pdf_text_cmd(510, $tableTop + 5, 9, 'TOTAL', 'F2', array(1,1,1)); $y = $tableTop - 27; $maxRows = 16; $shown = 0; foreach ($items as $it) { if ($shown >= $maxRows) break; if ($shown % 2 === 1) { $c .= sprintf("0.975 0.975 0.980 rg 32 %.2f 531 24 re f\n", $y - 7); } $c .= pdf_text_cmd(42, $y, 9, pdf_short($it['product_name'], 36), 'F1'); $c .= pdf_text_cmd(305, $y, 8.5, pdf_short($it['size'], 14), 'F1'); $c .= pdf_text_cmd(408, $y, 9, (string)(int)$it['quantity'], 'F1'); $c .= pdf_text_cmd(450, $y, 8.5, money($it['price']), 'F1'); $c .= pdf_text_cmd(510, $y, 8.5, money($it['price'] * $it['quantity']), 'F2'); $c .= sprintf("0.88 0.88 0.90 RG 32 %.2f m 563 %.2f l S\n", $y - 10, $y - 10); $y -= 27; $shown++; } if (count($items) > $maxRows) { $c .= pdf_text_cmd(42, $y, 8.5, '... y ' . (count($items) - $maxRows) . ' producto(s) mas', 'F1', array(0.40,0.40,0.43)); $y -= 24; } // Total y pie. $totalBoxY = max(82, $y - 52); $c .= sprintf("0.965 0.965 0.972 rg 335 %.2f 228 56 re f\n", $totalBoxY); $c .= pdf_text_cmd(350, $totalBoxY + 34, 9, 'TOTAL PAGADO', 'F2', array(0.45,0.45,0.48)); $c .= pdf_text_cmd(444, $totalBoxY + 20, 19, money($order['total']), 'F2', array(0.72,0.04,0.07)); $c .= pdf_text_cmd(38, 50, 9, 'Gracias por comprar en JAMA Store.', 'F2'); $c .= pdf_text_cmd(38, 34, 7.5, 'Comprobante interno de pedido. No reemplaza un comprobante electronico emitido ante SUNAT.', 'F1', array(0.46,0.46,0.48)); $objects = array(); $objects[] = "1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n"; $objects[] = "2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj\n"; $objects[] = "3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R /F2 6 0 R >> >> /Contents 4 0 R >> endobj\n"; $objects[] = "4 0 obj << /Length " . strlen($c) . " >> stream\n" . $c . "\nendstream\nendobj\n"; $objects[] = "5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj\n"; $objects[] = "6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >> endobj\n"; $pdf = "%PDF-1.4\n"; $offsets = array(0); foreach ($objects as $obj) { $offsets[] = strlen($pdf); $pdf .= $obj; } $xref = strlen($pdf); $pdf .= "xref\n0 " . (count($objects) + 1) . "\n0000000000 65535 f \n"; for ($i = 1; $i <= count($objects); $i++) { $pdf .= sprintf("%010d 00000 n \n", $offsets[$i]); } $pdf .= "trailer << /Size " . (count($objects) + 1) . " /Root 1 0 R >>\nstartxref\n" . $xref . "\n%%EOF"; return $pdf; } function admin_invoice_pdf() { require_admin(); $id = isset($_GET['id']) ? (int)$_GET['id'] : 0; $order = one("SELECT * FROM orders WHERE id = ?", array($id)); if (!$order || $order['payment_status'] !== 'Aprobado') { redirect_to('admin-pedidos', array('err' => 'Primero aprueba el pago.')); } $items = all_rows("SELECT * FROM order_items WHERE order_id = ?", array($id)); $pdf = jama_invoice_pdf($order, $items); header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="comprobante-jama-' . (int)$order['id'] . '.pdf"'); header('Content-Length: ' . strlen($pdf)); echo $pdf; exit; } function admin_backup() { require_admin(); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="jama-store-backup-' . date('Ymd-His') . '.sqlite"'); header('Content-Length: ' . filesize(DB_PATH)); readfile(DB_PATH); exit; } function legal_page($title) { $content = '
JAMA Store

' . e($title) . '

'; page($title, $content); } $route = current_route(); try { switch ($route) { case 'inicio': page_home(); break; case 'catalogo': page_catalog(false); break; case 'perfumes': page_catalog(true); break; case 'producto': page_product(); break; case 'promos': page_promos(); break; case 'carrito': page_cart(); break; case 'agregar': handle_add_to_cart(); break; case 'carrito-actualizar': handle_cart_update(); break; case 'carrito-eliminar': handle_cart_remove(); break; case 'pedido-crear': handle_create_order(); break; case 'pedido-exito': page_order_success(); break; case 'mis-pedidos': page_my_orders(); break; case 'pedido': page_customer_order(); break; case 'login': $_SERVER['REQUEST_METHOD'] === 'POST' ? handle_login(false) : page_login(false); break; case 'registro': $_SERVER['REQUEST_METHOD'] === 'POST' ? handle_register() : page_register(); break; case 'logout': handle_logout(); break; case 'google-login': handle_google_login(); break; case 'google-callback': handle_google_callback(); break; case 'admin-login': $_SERVER['REQUEST_METHOD'] === 'POST' ? handle_login(true) : page_login(true); break; case 'admin': admin_dashboard(); break; case 'admin-pedidos': admin_orders(); break; case 'admin-pedido': admin_order_detail(); break; case 'admin-pedido-estado': handle_admin_order_status(); break; case 'admin-pedido-eliminar': handle_admin_order_delete(); break; case 'admin-ganancia-reiniciar': handle_admin_earnings_reset(); break; case 'admin-productos': admin_products(); break; case 'admin-productos-masivo': admin_products_bulk_form(); break; case 'admin-productos-masivo-guardar': handle_products_bulk_save(); break; case 'admin-producto-form': admin_product_form(); break; case 'admin-producto-crear': handle_product_save(true); break; case 'admin-producto-guardar': handle_product_save(false); break; case 'admin-producto-eliminar': handle_product_active(false); break; case 'admin-producto-restaurar': handle_product_active(true); break; case 'admin-clientes': admin_clients(); break; case 'admin-promociones': admin_promotions(); break; case 'admin-promo-crear': handle_promotion_create(); break; case 'admin-redes': admin_socials(); break; case 'admin-redes-crear': handle_social_create(); break; case 'admin-redes-eliminar': handle_social_delete(); break; case 'admin-boleta': admin_invoice_pdf(); break; case 'admin-backup': admin_backup(); break; case 'comprobante': serve_receipt(); break; case 'configuracion-cookies': legal_page('Configuracion de las cookies'); break; case 'terminos-y-condiciones': legal_page('Terminos y Condiciones'); break; case 'terminos-promociones': legal_page('Terminos y Condiciones Promociones'); break; case 'politica-privacidad': legal_page('Politica de Privacidad'); break; case 'seleccionar-pais': legal_page('Seleccionar Pais'); break; case 'uso-del-sitio': legal_page('Uso del Sitio'); break; default: http_response_code(404); page('No encontrado', '
Pagina no encontrada.
'); } } catch (Exception $e) { http_response_code(500); page('Error', '

Error interno

Revisa que PHP tenga SQLite activo y que las carpetas data, uploads y private tengan permiso de escritura.

' . e($e->getMessage()) . '
'); } ?>