699 lines
33 KiB
HTML
699 lines
33 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>SP-NET</title>
|
||
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
||
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.css" />
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/glider-js@1.7.7/glider.min.js"></script>
|
||
|
|
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
||
|
|
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap">
|
||
|
|
<style>
|
||
|
|
:root {
|
||
|
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
|
|
--secondary-gradient: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
||
|
|
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||
|
|
--card-bg: rgba(255, 255, 255, 0.95);
|
||
|
|
--glass-bg: rgba(255, 255, 255, 0.1);
|
||
|
|
--backdrop-blur: blur(10px);
|
||
|
|
}
|
||
|
|
.glass-card {
|
||
|
|
background: var(--card-bg);
|
||
|
|
backdrop-filter: var(--backdrop-blur);
|
||
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
|
|
}
|
||
|
|
.gradient-text {
|
||
|
|
background: var(--primary-gradient);
|
||
|
|
-webkit-background-clip: text;
|
||
|
|
-webkit-text-fill-color: transparent;
|
||
|
|
background-clip: text;
|
||
|
|
}
|
||
|
|
.floating-label {
|
||
|
|
position: relative;
|
||
|
|
}
|
||
|
|
.floating-label input:focus + label,
|
||
|
|
.floating-label input:not(:placeholder-shown) + label {
|
||
|
|
transform: translateY(-1.5rem) scale(0.85);
|
||
|
|
color: #1e40af;
|
||
|
|
}
|
||
|
|
.floating-label label {
|
||
|
|
position: absolute;
|
||
|
|
left: 0.75rem;
|
||
|
|
top: 0.75rem;
|
||
|
|
transition: all 0.2s ease-in-out;
|
||
|
|
pointer-events: none;
|
||
|
|
color: #6b7280;
|
||
|
|
}
|
||
|
|
.card-hover {
|
||
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
|
|
}
|
||
|
|
.card-hover:hover {
|
||
|
|
transform: translateY(-4px);
|
||
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||
|
|
}
|
||
|
|
.btn-modern {
|
||
|
|
background: var(--primary-gradient);
|
||
|
|
transition: all 0.3s ease;
|
||
|
|
position: relative;
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
.btn-modern::before {
|
||
|
|
content: '';
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: -100%;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
||
|
|
transition: left 0.5s;
|
||
|
|
}
|
||
|
|
.btn-modern:hover::before {
|
||
|
|
left: 100%;
|
||
|
|
}
|
||
|
|
.btn-modern:hover {
|
||
|
|
transform: translateY(-2px);
|
||
|
|
box-shadow: 0 10px 20px rgba(30, 64, 175, 0.4);
|
||
|
|
}
|
||
|
|
.pulse-animation {
|
||
|
|
animation: pulse 2s infinite;
|
||
|
|
}
|
||
|
|
@keyframes pulse {
|
||
|
|
0%, 100% { opacity: 1; }
|
||
|
|
50% { opacity: 0.7; }
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body class="font-sans antialiased text-gray-900 bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100 min-h-screen font-inter">
|
||
|
|
<!-- Hero Section -->
|
||
|
|
<div class="relative overflow-hidden" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
|
||
|
|
<!-- Background Pattern -->
|
||
|
|
<div class="absolute inset-0 bg-gradient-to-br from-slate-600/20 via-gray-600/20 to-slate-700/20"></div>
|
||
|
|
<div class="absolute inset-0 bg-[url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.05"%3E%3Ccircle cx="30" cy="30" r="2"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E')] opacity-30"></div>
|
||
|
|
|
||
|
|
<div class="relative mx-auto max-w-screen-2xl px-4 py-4 md:px-6">
|
||
|
|
<div class="glass-card relative mx-auto max-w-2xl rounded-2xl p-4 shadow-xl backdrop-blur-xl">
|
||
|
|
<div class="text-center space-y-3">
|
||
|
|
<div class="space-y-1">
|
||
|
|
<h1 class="text-2xl md:text-3xl font-black text-gray-800 mb-1">SP-NET</h1>
|
||
|
|
<div class="w-16 h-0.5 bg-gray-600 mx-auto rounded-full"></div>
|
||
|
|
<p class="text-lg font-semibold text-gray-700">HOTSPOT LOGIN</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="bg-white/90 rounded-lg p-2 mb-1 shadow-sm">
|
||
|
|
<p class="text-xs text-gray-700 italic leading-relaxed">
|
||
|
|
Empowering businesses with reliable WiFi solutions and seamless connectivity across Kenya.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="bg-white/90 rounded-xl p-3 backdrop-blur-sm shadow-sm">
|
||
|
|
<h3 class="text-sm font-bold text-gray-800 mb-2">How to Connect</h3>
|
||
|
|
<div class="grid grid-cols-2 gap-2 text-gray-700">
|
||
|
|
<div class="flex items-center space-x-2">
|
||
|
|
<span class="flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold">1</span>
|
||
|
|
<span class="text-xs">Click on your preferred package</span>
|
||
|
|
</div>
|
||
|
|
<div class="flex items-center space-x-2">
|
||
|
|
<span class="flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold">2</span>
|
||
|
|
<span class="text-xs">Enter Phone No.</span>
|
||
|
|
</div>
|
||
|
|
<div class="flex items-center space-x-2">
|
||
|
|
<span class="flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold">3</span>
|
||
|
|
<span class="text-xs">Enter pin</span>
|
||
|
|
</div>
|
||
|
|
<div class="flex items-center space-x-2">
|
||
|
|
<span class="flex-shrink-0 w-5 h-5 bg-blue-500 text-white rounded-full flex items-center justify-center text-xs font-bold">4</span>
|
||
|
|
<span class="text-xs">Wait to be connected</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="bg-white/90 rounded-lg p-2 border border-gray-200/50 shadow-sm">
|
||
|
|
<p class="text-sm font-medium text-gray-700">
|
||
|
|
<i class="fas fa-phone-alt text-blue-500 mr-2"></i>
|
||
|
|
For any enquiries contact: <a href="tel:0748414608" class="font-bold text-blue-600 hover:text-blue-800 transition-colors duration-200">0748414608</a>
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<!-- Voucher Section -->
|
||
|
|
<div class="py-3 sm:py-4 lg:py-5">
|
||
|
|
<div class="mx-auto max-w-screen-2xl px-4 md:px-6">
|
||
|
|
<div class="mx-auto max-w-md">
|
||
|
|
<div class="glass-card rounded-xl p-4 shadow-lg backdrop-blur-xl">
|
||
|
|
<div class="text-center space-y-2">
|
||
|
|
<div class="w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center mx-auto">
|
||
|
|
<i class="fas fa-ticket-alt text-white text-lg"></i>
|
||
|
|
</div>
|
||
|
|
<h3 class="text-lg font-bold text-gray-800">Have a Voucher Code?</h3>
|
||
|
|
<p class="text-sm text-gray-600">Redeem your voucher for instant access</p>
|
||
|
|
<button type="button" class="w-full flex items-center justify-center gap-3 rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 px-8 py-4 text-center text-sm font-semibold text-white shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 focus:outline-none focus:ring-4 focus:ring-purple-300 md:text-base" onclick="redeemVoucher()">
|
||
|
|
<i class="fas fa-gift text-lg"></i>
|
||
|
|
Redeem Voucher
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<!-- Packages Section -->
|
||
|
|
<div class="py-3 sm:py-4 lg:py-5">
|
||
|
|
<div class="mx-auto max-w-screen-2xl px-4 md:px-6">
|
||
|
|
<div class="text-center mb-4">
|
||
|
|
<h2 class="text-xl font-bold text-gray-800 mb-1">Choose Your Package</h2>
|
||
|
|
<p class="text-sm text-gray-600">Select the perfect plan for your internet needs</p>
|
||
|
|
</div>
|
||
|
|
<div class="mx-auto max-w-6xl grid grid-cols-2 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4 gap-3" id="cards-container">
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<!-- Reconnection & Login Section -->
|
||
|
|
<div class="py-3 sm:py-4 lg:py-5">
|
||
|
|
<div class="mx-auto max-w-screen-2xl px-4 md:px-6">
|
||
|
|
<div class="max-w-2xl mx-auto space-y-4">
|
||
|
|
<!-- Reconnection Card -->
|
||
|
|
<div class="glass-card rounded-xl p-3 shadow-lg backdrop-blur-xl">
|
||
|
|
<div class="flex items-center gap-3 mb-3">
|
||
|
|
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center flex-shrink-0">
|
||
|
|
<i class="fas fa-redo text-white text-xs"></i>
|
||
|
|
</div>
|
||
|
|
<div class="flex-1 min-w-0">
|
||
|
|
<h3 class="text-base font-bold text-gray-800 truncate">Reconnect with Payment Code</h3>
|
||
|
|
<p class="text-sm text-gray-600">Enter your payment transaction code</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="flex gap-2">
|
||
|
|
<div class="flex-1">
|
||
|
|
<input type="text" id="mpesaCodeInput" name="mpesa_code" placeholder="Enter payment code..." class="w-full rounded-lg border-2 border-gray-200 bg-white/80 px-3 py-2 text-gray-800 outline-none ring-2 ring-transparent transition-all duration-200 focus:border-blue-500 focus:ring-blue-100 focus:bg-white text-sm" />
|
||
|
|
</div>
|
||
|
|
<button id="reconnectBtn" class="btn-modern flex items-center justify-center gap-1 rounded-lg px-4 py-2 text-white font-semibold transition-all duration-300 text-sm whitespace-nowrap">
|
||
|
|
<i class="fas fa-wifi text-sm"></i>
|
||
|
|
<span class="hidden sm:inline">Reconnect</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Login Card -->
|
||
|
|
<div class="glass-card rounded-xl p-3 shadow-lg backdrop-blur-xl">
|
||
|
|
<div class="flex items-center gap-3 mb-4">
|
||
|
|
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center flex-shrink-0">
|
||
|
|
<i class="fas fa-user-check text-white text-xs"></i>
|
||
|
|
</div>
|
||
|
|
<div class="flex-1 min-w-0">
|
||
|
|
<h3 class="text-base font-bold text-gray-800 truncate">Already Have an Active Package?</h3>
|
||
|
|
<p class="text-sm text-gray-600">Login with your account details</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<form id="loginForm" class="space-y-3" name="login" action="$(link-login-only)" method="post" $(if chap-id)onSubmit="return doLogin()" $(endif)>
|
||
|
|
<input type="hidden" name="dst" value="$(link-orig)" />
|
||
|
|
<input type="hidden" name="popup" value="true" />
|
||
|
|
<div class="flex gap-2">
|
||
|
|
<div class="flex-1">
|
||
|
|
<input id="usernameInput" name="username" type="text" value="" placeholder="Enter username or account number..." class="w-full rounded-lg border-2 border-gray-200 bg-white/80 px-3 py-2 text-gray-800 outline-none ring-2 ring-transparent transition-all duration-200 focus:border-green-500 focus:ring-green-100 focus:bg-white text-sm" />
|
||
|
|
</div>
|
||
|
|
<button id="submitBtn" class="btn-modern flex items-center justify-center gap-1 rounded-lg px-4 py-2 text-white font-semibold transition-all duration-300 text-sm whitespace-nowrap" type="button" onclick="submitLogin()">
|
||
|
|
<i class="fas fa-sign-in-alt text-sm"></i>
|
||
|
|
<span class="hidden sm:inline">Connect</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<input type="hidden" name="password" value="1234">
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<!-- Footer -->
|
||
|
|
<div class="py-2 sm:py-3">
|
||
|
|
<div class="mx-auto max-w-screen-2xl px-4 md:px-6">
|
||
|
|
<div class="glass-card rounded-xl p-3 text-center backdrop-blur-xl">
|
||
|
|
<div class="flex items-center justify-center space-x-2 mb-2">
|
||
|
|
<div class="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
|
||
|
|
<i class="fas fa-wifi text-white text-xs"></i>
|
||
|
|
</div>
|
||
|
|
<span class="text-base font-bold text-gray-800">Powered by NestICT</span>
|
||
|
|
</div>
|
||
|
|
<p class="text-xs text-gray-600 mb-2">
|
||
|
|
© 2026 Billing System
|
||
|
|
</p>
|
||
|
|
<div class="flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-4 mb-2">
|
||
|
|
<a href="tel:+256780327582" class="inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200 text-xs">
|
||
|
|
<i class="fas fa-phone text-blue-500 mr-1"></i>
|
||
|
|
<span>+256780327582</span>
|
||
|
|
</a>
|
||
|
|
<a href="mailto:sales@nestict.net" class="inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200 text-xs">
|
||
|
|
<i class="fas fa-envelope text-blue-500 mr-1"></i>
|
||
|
|
<span>sales@nestict.net</span>
|
||
|
|
</a>
|
||
|
|
<a href="https://wa.me/+256780327582" target="_blank" class="inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200 text-xs">
|
||
|
|
<i class="fab fa-whatsapp text-blue-500 mr-1"></i>
|
||
|
|
<span>WhatsApp</span>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
<a href="https://www.nestict.africa/" target="_blank" class="inline-flex items-center text-blue-600 hover:text-blue-800 transition-colors duration-200 text-xs">
|
||
|
|
<span class="mr-1">Visit NestICT</span>
|
||
|
|
<i class="fas fa-external-link-alt text-xs"></i>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
<script>
|
||
|
|
</script>
|
||
|
|
<script>
|
||
|
|
function fetchData() {
|
||
|
|
let domain = 'https://www.spnett.nestict.net/';
|
||
|
|
let siteUrl = domain + "/index.php?_route=plugin/hotspot_plan";
|
||
|
|
let request = new XMLHttpRequest();
|
||
|
|
const routerName = encodeURIComponent("SPNET-ICD");
|
||
|
|
const dataparams = `routername=${routerName}`;
|
||
|
|
request.open("POST", siteUrl, true);
|
||
|
|
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||
|
|
request.onload = () => {
|
||
|
|
if (request.readyState === XMLHttpRequest.DONE) {
|
||
|
|
if (request.status === 200) {
|
||
|
|
let fetchedData = JSON.parse(request.responseText);
|
||
|
|
populateCards(fetchedData);
|
||
|
|
} else {
|
||
|
|
console.log(`Error ${request.status}: ${request.statusText}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
request.onerror = () => {
|
||
|
|
console.error("Network error");
|
||
|
|
};
|
||
|
|
request.send(dataparams);
|
||
|
|
}
|
||
|
|
function populateCards(data) {
|
||
|
|
var cardsContainer = document.getElementById('cards-container');
|
||
|
|
cardsContainer.innerHTML = ''; // Clear existing content
|
||
|
|
// Sort the plans by price in ascending order
|
||
|
|
data.data.forEach(router => {
|
||
|
|
// Sort hotspot plans by price
|
||
|
|
router.plans_hotspot.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
|
||
|
|
router.plans_hotspot.forEach(item => {
|
||
|
|
var cardDiv = document.createElement('div');
|
||
|
|
cardDiv.className = 'glass-card card-hover rounded-2xl shadow-xl backdrop-blur-xl overflow-hidden flex flex-col mx-auto w-full max-w-xs group';
|
||
|
|
cardDiv.innerHTML = `
|
||
|
|
<div class="relative bg-gradient-to-r from-blue-500 to-blue-600 text-white p-3">
|
||
|
|
<div class="absolute top-3 right-3 w-3 h-3 bg-white/30 rounded-full"></div>
|
||
|
|
<h2 class="text-base font-bold text-center mb-2" style="font-size: clamp(0.875rem, 2vw, 1.125rem); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||
|
|
${item.planname}
|
||
|
|
</h2>
|
||
|
|
<div class="w-12 h-0.5 bg-white/50 rounded-full mx-auto"></div>
|
||
|
|
</div>
|
||
|
|
<div class="p-3 flex-grow flex flex-col justify-between">
|
||
|
|
<div class="text-center mb-3">
|
||
|
|
<div class="mb-2">
|
||
|
|
<span class="text-2xl font-black text-gray-800">${item.currency}</span>
|
||
|
|
<span class="text-3xl font-black text-blue-600">${item.price}</span>
|
||
|
|
</div>
|
||
|
|
<div class="bg-gradient-to-r from-slate-50 to-gray-100 rounded-lg p-2">
|
||
|
|
<p class="text-sm font-semibold text-gray-700 flex items-center justify-center">
|
||
|
|
<i class="fas fa-clock text-blue-500 mr-2"></i>
|
||
|
|
Valid for ${item.validity} ${item.timelimit}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<button class="w-full bg-gradient-to-r from-gray-800 to-gray-900 text-white hover:from-gray-900 hover:to-black font-semibold py-3 px-4 rounded-xl transition-all duration-200 transform hover:-translate-y-0.5 shadow-lg hover:shadow-xl flex items-center justify-center gap-2 text-sm"
|
||
|
|
onclick="handlePhoneNumberSubmission('${item.planId}', '${item.routerId}', '${item.price}'); return false;"
|
||
|
|
data-plan-id="${item.planId}"
|
||
|
|
data-router-id="${item.routerId}">
|
||
|
|
<i class="fas fa-shopping-cart text-base"></i>
|
||
|
|
<span>Buy Now</span>
|
||
|
|
</button>
|
||
|
|
`;
|
||
|
|
cardsContainer.appendChild(cardDiv);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
fetchData();
|
||
|
|
</script>
|
||
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||
|
|
<script src="https://www.spnett.nestict.net/system/plugin/js/device-fingerprint.js"></script>
|
||
|
|
<script>
|
||
|
|
function formatPhoneNumber(phoneNumber) {
|
||
|
|
if (phoneNumber.startsWith('+')) {
|
||
|
|
phoneNumber = phoneNumber.substring(1);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get payment gateway from server
|
||
|
|
var paymentGateway = 'julypay';
|
||
|
|
|
||
|
|
// Check if julypay is in the payment gateway list
|
||
|
|
var isJulypay = paymentGateway.includes('julypay');
|
||
|
|
|
||
|
|
if (phoneNumber.startsWith('0')) {
|
||
|
|
if (isJulypay) {
|
||
|
|
phoneNumber = '256' + phoneNumber.substring(1);
|
||
|
|
} else {
|
||
|
|
phoneNumber = '254' + phoneNumber.substring(1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (phoneNumber.match(/^(7|1)/)) {
|
||
|
|
if (isJulypay) {
|
||
|
|
phoneNumber = '256' + phoneNumber;
|
||
|
|
} else {
|
||
|
|
phoneNumber = '254' + phoneNumber;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return phoneNumber;
|
||
|
|
}
|
||
|
|
|
||
|
|
function setCookie(name, value, days) {
|
||
|
|
var expires = "";
|
||
|
|
if (days) {
|
||
|
|
var date = new Date();
|
||
|
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||
|
|
expires = "; expires=" + date.toUTCString();
|
||
|
|
}
|
||
|
|
document.cookie = name + "=" + (value || "") + expires + "; path=/";
|
||
|
|
}
|
||
|
|
|
||
|
|
function getCookie(name) {
|
||
|
|
var nameEQ = name + "=";
|
||
|
|
var ca = document.cookie.split(';');
|
||
|
|
for (var i = 0; i < ca.length; i++) {
|
||
|
|
var c = ca[i];
|
||
|
|
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||
|
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function generateAccountId() {
|
||
|
|
return 'ACC' + Math.floor(10000 + Math.random() * 90000); // Generate a random number between 10000 and 99999
|
||
|
|
}
|
||
|
|
|
||
|
|
var loginTimeout; // Variable to store the timeout ID
|
||
|
|
function handlePhoneNumberSubmission(planId, routerId, price) {
|
||
|
|
var msg = "You are about to pay Ugx: " + price + ". Enter phone number below and click pay now to initialize payment";
|
||
|
|
const regexp = /\${([^{}]+)}/g;
|
||
|
|
let result = msg.replace(regexp, function(ignore, key) {
|
||
|
|
return eval(key);
|
||
|
|
});
|
||
|
|
swal.fire({
|
||
|
|
title: 'Enter Your Phone Number',
|
||
|
|
input: 'number',
|
||
|
|
inputAttributes: {
|
||
|
|
required: 'true'
|
||
|
|
},
|
||
|
|
inputValidator: function(value) {
|
||
|
|
if (value === '') {
|
||
|
|
return 'You need to write your phonenumber!';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
text: result,
|
||
|
|
showCancelButton: true,
|
||
|
|
confirmButtonColor: '#3085d6',
|
||
|
|
cancelButtonColor: '#d33',
|
||
|
|
confirmButtonText: 'Pay Now',
|
||
|
|
showLoaderOnConfirm: true,
|
||
|
|
preConfirm: (phoneNumber) => {
|
||
|
|
var formattedPhoneNumber = formatPhoneNumber(phoneNumber);
|
||
|
|
var accountId = getCookie('accountId');
|
||
|
|
if (!accountId) {
|
||
|
|
accountId = generateAccountId(); // Generate a new account ID
|
||
|
|
setCookie('accountId', accountId, 7); // Set account ID as a cookie
|
||
|
|
}
|
||
|
|
document.getElementById('usernameInput').value = accountId; // Use account ID as the new username
|
||
|
|
console.log("Phone number for autofill:", formattedPhoneNumber);
|
||
|
|
|
||
|
|
// Get device information for enhanced tracking
|
||
|
|
var deviceInfo = null;
|
||
|
|
if (window.deviceFingerprint) {
|
||
|
|
deviceInfo = window.deviceFingerprint.getDeviceInfo();
|
||
|
|
console.log('Device fingerprinting available:', deviceInfo.deviceId);
|
||
|
|
} else {
|
||
|
|
console.log('Device fingerprinting not available');
|
||
|
|
}
|
||
|
|
|
||
|
|
return fetch('https://www.spnett.nestict.net/index.php?_route=plugin/CreateHotspotUser&type=grant', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {'Content-Type': 'application/json'},
|
||
|
|
body: JSON.stringify({
|
||
|
|
phone_number: formattedPhoneNumber,
|
||
|
|
plan_id: planId,
|
||
|
|
router_id: routerId,
|
||
|
|
account_id: accountId,
|
||
|
|
device_id: deviceInfo ? deviceInfo.deviceId : null,
|
||
|
|
device_fingerprint: deviceInfo ? deviceInfo.fingerprint : null
|
||
|
|
}),
|
||
|
|
})
|
||
|
|
.then(response => {
|
||
|
|
if (!response.ok) throw new Error('Network response was not ok');
|
||
|
|
return response.json();
|
||
|
|
})
|
||
|
|
.then(data => {
|
||
|
|
if (data.status === 'error') throw new Error(data.message);
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Processing..',
|
||
|
|
html: `A payment request has been sent to your phone. Please wait while we process your payment.`,
|
||
|
|
showConfirmButton: false,
|
||
|
|
allowOutsideClick: false,
|
||
|
|
didOpen: () => {
|
||
|
|
Swal.showLoading();
|
||
|
|
checkPaymentStatus(formattedPhoneNumber);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return formattedPhoneNumber;
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
console.error('Payment initiation error:', error);
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Oops...',
|
||
|
|
text: error.message || 'An error occurred while processing your payment. Please try again.',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
},
|
||
|
|
allowOutsideClick: () => !Swal.isLoading()
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function checkPaymentStatus(phoneNumber) {
|
||
|
|
let checkInterval = setInterval(() => {
|
||
|
|
$.ajax({
|
||
|
|
url: 'https://www.spnett.nestict.net/index.php?_route=plugin/CreateHotspotUser&type=verify',
|
||
|
|
method: 'POST',
|
||
|
|
data: JSON.stringify({account_id: document.getElementById('usernameInput').value}),
|
||
|
|
contentType: 'application/json',
|
||
|
|
dataType: 'json',
|
||
|
|
success: function(data) {
|
||
|
|
// Raw Response
|
||
|
|
if (data.Resultcode === '3') { // Success
|
||
|
|
clearInterval(checkInterval);
|
||
|
|
Swal.fire({
|
||
|
|
icon: 'success',
|
||
|
|
title: 'Payment Successful',
|
||
|
|
text: data.Message,
|
||
|
|
showConfirmButton: false
|
||
|
|
});
|
||
|
|
if (loginTimeout) {
|
||
|
|
clearTimeout(loginTimeout);
|
||
|
|
}
|
||
|
|
loginTimeout = setTimeout(function() {
|
||
|
|
document.getElementById('loginForm').submit();
|
||
|
|
}, 2000);
|
||
|
|
} else if (data.Resultcode === '2') { // Error
|
||
|
|
clearInterval(checkInterval);
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Payment Issue',
|
||
|
|
text: data.Message,
|
||
|
|
});
|
||
|
|
} else if (data.Resultcode === '1') { // Primary
|
||
|
|
// Continue checking
|
||
|
|
}
|
||
|
|
},
|
||
|
|
error: function(xhr, textStatus, errorThrown) {
|
||
|
|
// Error occurred
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}, 2000);
|
||
|
|
|
||
|
|
setTimeout(() => {
|
||
|
|
clearInterval(checkInterval);
|
||
|
|
Swal.fire({
|
||
|
|
icon: 'warning',
|
||
|
|
title: 'Timeout',
|
||
|
|
text: 'Payment verification timed out. Please try again.',
|
||
|
|
});
|
||
|
|
}, 600000); // Stop checking after 60 seconds
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
</script>
|
||
|
|
<script>
|
||
|
|
var loginTimeout; // Variable to store the timeout ID
|
||
|
|
function redeemVoucher() {
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Redeem Voucher',
|
||
|
|
input: 'text',
|
||
|
|
inputPlaceholder: 'Enter voucher code',
|
||
|
|
inputValidator: function(value) {
|
||
|
|
if (!value) {
|
||
|
|
return 'You need to enter a voucher code!';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
confirmButtonColor: '#3085d6',
|
||
|
|
cancelButtonColor: '#d33',
|
||
|
|
confirmButtonText: 'Redeem',
|
||
|
|
showLoaderOnConfirm: true,
|
||
|
|
preConfirm: (voucherCode) => {
|
||
|
|
var accountId = voucherCode;
|
||
|
|
if (!accountId) {
|
||
|
|
accountId = voucherCode;
|
||
|
|
setCookie('accountId', accountId, 7);
|
||
|
|
}
|
||
|
|
return fetch('https://www.spnett.nestict.net/index.php?_route=plugin/CreateHotspotUser&type=voucher', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {'Content-Type': 'application/json'},
|
||
|
|
body: JSON.stringify({voucher_code: voucherCode, account_id: accountId}),
|
||
|
|
})
|
||
|
|
.then(response => {
|
||
|
|
if (!response.ok) throw new Error('Network response was not ok');
|
||
|
|
return response.json();
|
||
|
|
})
|
||
|
|
.then(data => {
|
||
|
|
if (data.status === 'error') throw new Error(data.message);
|
||
|
|
return data;
|
||
|
|
});
|
||
|
|
},
|
||
|
|
allowOutsideClick: () => !Swal.isLoading()
|
||
|
|
}).then((result) => {
|
||
|
|
if (result.isConfirmed) {
|
||
|
|
Swal.fire({
|
||
|
|
icon: 'success',
|
||
|
|
title: 'Voucher Redeemed',
|
||
|
|
text: result.value.message,
|
||
|
|
showConfirmButton: false,
|
||
|
|
allowOutsideClick: false,
|
||
|
|
didOpen: () => {
|
||
|
|
Swal.showLoading();
|
||
|
|
var username = result.value.username;
|
||
|
|
// Received username from server
|
||
|
|
var usernameInput = document.querySelector('input[name="username"]');
|
||
|
|
if (usernameInput) {
|
||
|
|
// Found username input element
|
||
|
|
usernameInput.value = username;
|
||
|
|
loginTimeout = setTimeout(function() {
|
||
|
|
var loginForm = document.getElementById('loginForm');
|
||
|
|
if (loginForm) {
|
||
|
|
loginForm.submit();
|
||
|
|
} else {
|
||
|
|
// Login form not found
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Error',
|
||
|
|
text: 'Login form not found. Please try again.',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}, 2000);
|
||
|
|
} else {
|
||
|
|
// Username input element not found
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Error',
|
||
|
|
text: 'Username input not found. Please try again.',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}).catch(error => {
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Oops...',
|
||
|
|
text: error.message,
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
<script>
|
||
|
|
var loginTimeout; // Variable to store the timeout ID
|
||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
||
|
|
document.getElementById('reconnectBtn').addEventListener('click', function() {
|
||
|
|
var paymentCode = document.getElementById('mpesaCodeInput').value;
|
||
|
|
var firstWord = paymentCode.split(' ')[0]; // Get the first word in the payment code
|
||
|
|
fetch('https://www.spnett.nestict.net/index.php?_route=plugin/CreateHotspotUser&type=reconnect', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {'Content-Type': 'application/json'},
|
||
|
|
body: JSON.stringify({mpesa_code: firstWord}),
|
||
|
|
})
|
||
|
|
.then(response => response.json())
|
||
|
|
.then(data => {
|
||
|
|
if (data.Status === 'success') {
|
||
|
|
Swal.fire({
|
||
|
|
icon: 'success',
|
||
|
|
title: 'Reconnection Successful',
|
||
|
|
text: data.Message,
|
||
|
|
showConfirmButton: false,
|
||
|
|
allowOutsideClick: false,
|
||
|
|
didOpen: () => {
|
||
|
|
Swal.showLoading();
|
||
|
|
var username = data.username; // Replace with actual JSON field name
|
||
|
|
// Received username from server
|
||
|
|
var usernameInput = document.querySelector('input[name="username"]');
|
||
|
|
if (usernameInput) {
|
||
|
|
// Found username input element
|
||
|
|
usernameInput.value = username;
|
||
|
|
loginTimeout = setTimeout(function() {
|
||
|
|
var loginForm = document.getElementById('loginForm');
|
||
|
|
if (loginForm) {
|
||
|
|
loginForm.submit();
|
||
|
|
} else {
|
||
|
|
// Login form not found
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Error',
|
||
|
|
text: 'Login form not found. Please try again.',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}, 2000);
|
||
|
|
} else {
|
||
|
|
// Username input element not found
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Error',
|
||
|
|
text: 'Username input not found. Please try again.',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Reconnection Failed',
|
||
|
|
text: data.Message,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
})
|
||
|
|
.catch(error => {
|
||
|
|
// Error occurred
|
||
|
|
Swal.fire({
|
||
|
|
title: 'Error',
|
||
|
|
text: 'Failed to reconnect. Please try again later.',
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||
|
|
<script>
|
||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
||
|
|
// Ensure the button is correctly targeted by its ID.
|
||
|
|
var submitBtn = document.getElementById('submitBtn');
|
||
|
|
|
||
|
|
// Add a click event listener to the "Login Now" button.
|
||
|
|
submitBtn.addEventListener('click', function(event) {
|
||
|
|
event.preventDefault(); // Prevent the default button action.
|
||
|
|
|
||
|
|
// Optional: Log to console for debugging purposes.
|
||
|
|
// Login Now button clicked
|
||
|
|
|
||
|
|
// Direct form submission, bypassing the doLogin function for simplicity.
|
||
|
|
var form = document.getElementById('loginForm');
|
||
|
|
form.submit(); // Submit the form directly.
|
||
|
|
});
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
</html>
|