online payment kaspi
This commit is contained in:
parent
c6f5baae6e
commit
d5ca8012e6
140
index.html
140
index.html
@ -23,6 +23,9 @@ body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
|||||||
.card-title{font-size:20px;font-weight:700;margin-bottom:8px}
|
.card-title{font-size:20px;font-weight:700;margin-bottom:8px}
|
||||||
.card-price{font-size:24px;font-weight:800;color:var(--ink);margin-bottom:12px}
|
.card-price{font-size:24px;font-weight:800;color:var(--ink);margin-bottom:12px}
|
||||||
.card-cat{display:inline-block;background:var(--cyan-50);color:var(--ink);padding:4px 12px;border-radius:20px;font-size:14px;font-weight:600;margin-bottom:12px}
|
.card-cat{display:inline-block;background:var(--cyan-50);color:var(--ink);padding:4px 12px;border-radius:20px;font-size:14px;font-weight:600;margin-bottom:12px}
|
||||||
|
.status-badge{display:inline-block;padding:4px 12px;border-radius:20px;font-size:13px;font-weight:600;margin-left:8px}
|
||||||
|
.status-paid{background:#d4edda;color:#155724}
|
||||||
|
.status-waiting{background:#fff3cd;color:#856404}
|
||||||
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none;border:none;cursor:pointer}
|
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none;border:none;cursor:pointer}
|
||||||
.btn:hover{opacity:0.9}
|
.btn:hover{opacity:0.9}
|
||||||
.btn-green{background:var(--green);color:#fff}
|
.btn-green{background:var(--green);color:#fff}
|
||||||
@ -85,6 +88,7 @@ body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
|||||||
<div style="text-align:center;margin-top:40px;display:flex;gap:16px;justify-content:center;flex-wrap:wrap">
|
<div style="text-align:center;margin-top:40px;display:flex;gap:16px;justify-content:center;flex-wrap:wrap">
|
||||||
<button class="btn" onclick="openParentCodeModal()" style="background:var(--green);color:#fff">👨👩👧 Получить код родителя</button>
|
<button class="btn" onclick="openParentCodeModal()" style="background:var(--green);color:#fff">👨👩👧 Получить код родителя</button>
|
||||||
<button class="btn" onclick="showPage('admin');setTimeout(() => document.getElementById('nav-admin').click(), 100)" style="background:var(--ink);color:#fff">➕ Добавить товар</button>
|
<button class="btn" onclick="showPage('admin');setTimeout(() => document.getElementById('nav-admin').click(), 100)" style="background:var(--ink);color:#fff">➕ Добавить товар</button>
|
||||||
|
<button class="btn" onclick="openPaymentModal()" style="background:#FF5500;color:#fff">💳 Родителям: оплатить онлайн</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -130,6 +134,12 @@ body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
|||||||
<div id="parents-list" style="margin-bottom:32px"></div>
|
<div id="parents-list" style="margin-bottom:32px"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-panel">
|
||||||
|
<h3>📋 Предоплаченные заказы (онлайн оплата)</h3>
|
||||||
|
<p style="margin-bottom:16px;color:var(--gray-500)">Родитель оплатил онлайн → ребёнок показывает код → забирает товар</p>
|
||||||
|
<div id="orders-list" style="margin-top:16px"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="admin-panel">
|
<div class="admin-panel">
|
||||||
<h3>➕ Добавить товар</h3>
|
<h3>➕ Добавить товар</h3>
|
||||||
<form id="add-form">
|
<form id="add-form">
|
||||||
@ -193,6 +203,24 @@ body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,s
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" id="payment-modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h3>💳 Онлайн оплата</h3>
|
||||||
|
<p style="margin-bottom:16px">Родитель оплачивает — ребёнок забирает</p>
|
||||||
|
<div style="background:var(--cyan-50);padding:16px;border-radius:8px;margin-bottom:16px">
|
||||||
|
<strong>Товар:</strong> <span id="pay-product"></span><br>
|
||||||
|
<strong>Цена:</strong> <span id="pay-price"></span> ₸
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Телефон родителя (для входа)</label>
|
||||||
|
<input type="tel" id="pay-phone" placeholder="+7 777 123 45 67" style="width:100%;padding:12px;border:1px solid #ddd;border-radius:8px">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-green" onclick="processPayment()" style="width:100%">💳 Оплатить через Kaspi Pay</button>
|
||||||
|
<button class="btn" style="background:var(--gray-100);margin-top:12px;width:100%" onclick="closePaymentModal()">Отмена</button>
|
||||||
|
<p style="margin-top:16px;font-size:13px;color:var(--gray-500)">🔒 Тестовый режим — оплата не списывается</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal" id="success-modal">
|
<div class="modal" id="success-modal">
|
||||||
<div class="modal-content" style="text-align:center">
|
<div class="modal-content" style="text-align:center">
|
||||||
<h3>✅ Покупка разрешена!</h3>
|
<h3>✅ Покупка разрешена!</h3>
|
||||||
@ -209,6 +237,7 @@ const CATEGORIES = {еда:'🍫', напитки:'🥤', школа:'✏️',
|
|||||||
let products = [];
|
let products = [];
|
||||||
let parents = [];
|
let parents = [];
|
||||||
let purchases = [];
|
let purchases = [];
|
||||||
|
let orders = []; // Предоплаченные заказы
|
||||||
let isAdmin = false;
|
let isAdmin = false;
|
||||||
const ADMIN_PASS = 'admin123'; // Поменяй на свой!
|
const ADMIN_PASS = 'admin123'; // Поменяй на свой!
|
||||||
|
|
||||||
@ -218,10 +247,12 @@ function loadProducts() {
|
|||||||
products = localStorage.getItem('shop-products') ? JSON.parse(localStorage.getItem('shop-products')) : getDefaults();
|
products = localStorage.getItem('shop-products') ? JSON.parse(localStorage.getItem('shop-products')) : getDefaults();
|
||||||
parents = localStorage.getItem('shop-parents') ? JSON.parse(localStorage.getItem('shop-parents')) : [];
|
parents = localStorage.getItem('shop-parents') ? JSON.parse(localStorage.getItem('shop-parents')) : [];
|
||||||
purchases = localStorage.getItem('shop-purchases') ? JSON.parse(localStorage.getItem('shop-purchases')) : [];
|
purchases = localStorage.getItem('shop-purchases') ? JSON.parse(localStorage.getItem('shop-purchases')) : [];
|
||||||
|
orders = localStorage.getItem('shop-orders') ? JSON.parse(localStorage.getItem('shop-orders')) : [];
|
||||||
} catch {
|
} catch {
|
||||||
products = getDefaults();
|
products = getDefaults();
|
||||||
parents = [];
|
parents = [];
|
||||||
purchases = [];
|
purchases = [];
|
||||||
|
orders = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,6 +260,7 @@ function saveProducts() {
|
|||||||
localStorage.setItem('shop-products', JSON.stringify(products));
|
localStorage.setItem('shop-products', JSON.stringify(products));
|
||||||
localStorage.setItem('shop-parents', JSON.stringify(parents));
|
localStorage.setItem('shop-parents', JSON.stringify(parents));
|
||||||
localStorage.setItem('shop-purchases', JSON.stringify(purchases));
|
localStorage.setItem('shop-purchases', JSON.stringify(purchases));
|
||||||
|
localStorage.setItem('shop-orders', JSON.stringify(orders));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaults() {
|
function getDefaults() {
|
||||||
@ -263,7 +295,10 @@ function render(filter = 'all') {
|
|||||||
<div class="card-title">${p.name}</div>
|
<div class="card-title">${p.name}</div>
|
||||||
<div class="card-price">${p.price} ₸</div>
|
<div class="card-price">${p.price} ₸</div>
|
||||||
<p style="color:var(--gray-500);font-size:14px">${p.desc || ''}</p>
|
<p style="color:var(--gray-500);font-size:14px">${p.desc || ''}</p>
|
||||||
<button class="btn" style="margin-top:12px;width:100%" onclick="buyProduct(${p.id})">Купить</button>
|
<div style="display:flex;gap:8px;margin-top:12px">
|
||||||
|
<button class="btn" style="flex:1" onclick="buyProduct(${p.id})">Купить</button>
|
||||||
|
<button class="btn btn-green" style="flex:1" onclick="openPaymentModalForProduct(${JSON.stringify(p).replace(/"/g, '"')})">💳 Оплатить</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
@ -311,6 +346,44 @@ function render(filter = 'all') {
|
|||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Предоплаченные заказы
|
||||||
|
const ordersList = document.getElementById('orders-list');
|
||||||
|
if (ordersList) {
|
||||||
|
const activeOrders = orders.filter(o => !o.pickedUp);
|
||||||
|
const completedOrders = orders.filter(o => o.pickedUp);
|
||||||
|
|
||||||
|
if (activeOrders.length === 0 && completedOrders.length === 0) {
|
||||||
|
ordersList.innerHTML = '<p style="color:var(--gray-500)">Предоплаченных заказов пока нет</p>';
|
||||||
|
} else {
|
||||||
|
let html = '';
|
||||||
|
if (activeOrders.length > 0) {
|
||||||
|
html += '<h5 style="margin:16px 0 8px;color:var(--green)">⏳ Ожидают выдачи:</h5>';
|
||||||
|
html += activeOrders.map(o => `
|
||||||
|
<div class="card" style="margin-bottom:12px;padding:16px;border-left:4px solid var(--green)">
|
||||||
|
<strong>${o.product}</strong>
|
||||||
|
<span class="status-badge status-waiting">⏳ Ждёт выдачи</span><br>
|
||||||
|
<span style="color:var(--gray-500)">Родитель: ${o.parentName} (${o.parentPhone})</span><br>
|
||||||
|
<span style="color:var(--gray-500)">Код выдачи: <strong style="color:var(--cyan);font-size:20px">${o.code}</strong></span><br>
|
||||||
|
<span style="color:var(--gray-500)">Оплачено: ${o.price} ₸ | ${new Date(o.date).toLocaleString('ru-RU')}</span><br>
|
||||||
|
<button class="btn btn-green" style="margin-top:12px;padding:8px 16px" onclick="markAsPickedUp(${o.id})">✅ Товар выдан</button>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
if (completedOrders.length > 0) {
|
||||||
|
html += '<h5 style="margin:16px 0 8px;color:var(--gray-500)">✅ Выданные:</h5>';
|
||||||
|
html += completedOrders.slice(-5).reverse().map(o => `
|
||||||
|
<div class="card" style="margin-bottom:12px;padding:16px;border-left:4px solid #28a745">
|
||||||
|
<strong>${o.product}</strong>
|
||||||
|
<span class="status-badge status-paid">✅ Выдан</span><br>
|
||||||
|
<span style="color:var(--gray-500)">Родитель: ${o.parentName} | Код: ${o.code}</span><br>
|
||||||
|
<span style="color:var(--gray-500)">Выдан: ${new Date(o.pickedUpAt).toLocaleString('ru-RU')}</span>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
ordersList.innerHTML = html;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSpentToday(parentId) {
|
function getSpentToday(parentId) {
|
||||||
@ -394,6 +467,58 @@ function closeParentModal() {
|
|||||||
document.getElementById('parent-code-modal').classList.remove('on');
|
document.getElementById('parent-code-modal').classList.remove('on');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentProductForPayment = null;
|
||||||
|
|
||||||
|
function openPaymentModal() {
|
||||||
|
document.getElementById('payment-modal').classList.add('on');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePaymentModal() {
|
||||||
|
document.getElementById('payment-modal').classList.remove('on');
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPaymentModalForProduct(product) {
|
||||||
|
currentProductForPayment = product;
|
||||||
|
document.getElementById('pay-product').textContent = product.name;
|
||||||
|
document.getElementById('pay-price').textContent = product.price;
|
||||||
|
document.getElementById('payment-modal').classList.add('on');
|
||||||
|
}
|
||||||
|
|
||||||
|
function processPayment() {
|
||||||
|
const phone = document.getElementById('pay-phone').value.trim();
|
||||||
|
if (!phone || !currentProductForPayment) {
|
||||||
|
alert('Введите номер телефона и выберите товар');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Имитация оплаты Kaspi Pay
|
||||||
|
const orderCode = Math.floor(100000 + Math.random() * 900000).toString();
|
||||||
|
const order = {
|
||||||
|
id: Date.now(),
|
||||||
|
productId: currentProductForPayment.id,
|
||||||
|
product: currentProductForPayment.name,
|
||||||
|
price: currentProductForPayment.price,
|
||||||
|
parentPhone: phone,
|
||||||
|
parentName: phone, // Потом родитель представится при получении
|
||||||
|
code: orderCode,
|
||||||
|
date: new Date().toISOString(),
|
||||||
|
pickedUp: false,
|
||||||
|
pickedUpAt: null
|
||||||
|
};
|
||||||
|
|
||||||
|
orders.push(order);
|
||||||
|
saveProducts();
|
||||||
|
|
||||||
|
closePaymentModal();
|
||||||
|
|
||||||
|
document.getElementById('success-message').textContent = `✅ Оплата прошла успешно!\n\nВаш код выдачи: ${orderCode}\n\nПокажите этот код в магазине и получите товар.`;
|
||||||
|
document.getElementById('success-code').textContent = orderCode;
|
||||||
|
document.getElementById('success-modal').classList.add('on');
|
||||||
|
|
||||||
|
currentProductForPayment = null;
|
||||||
|
document.getElementById('pay-phone').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
function verifyParentCode() {
|
function verifyParentCode() {
|
||||||
const code = document.getElementById('parent-code-input').value.trim();
|
const code = document.getElementById('parent-code-input').value.trim();
|
||||||
const parent = parents.find(p => p.code === code);
|
const parent = parents.find(p => p.code === code);
|
||||||
@ -430,6 +555,19 @@ function verifyParentCode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markAsPickedUp(orderId) {
|
||||||
|
if (confirm('Подтвердить выдачу товара?')) {
|
||||||
|
const order = orders.find(o => o.id === orderId);
|
||||||
|
if (order) {
|
||||||
|
order.pickedUp = true;
|
||||||
|
order.pickedUpAt = new Date().toISOString();
|
||||||
|
saveProducts();
|
||||||
|
render();
|
||||||
|
alert('✅ Товар выдан!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === Родители ===
|
// === Родители ===
|
||||||
document.getElementById('parent-form').addEventListener('submit', (e) => {
|
document.getElementById('parent-form').addEventListener('submit', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user