vpn-btcpay-provisioner/app/static/js/pricing.js
2024-12-30 06:03:07 +00:00

137 lines
4.7 KiB
JavaScript

// Base64 encoding/decoding utilities
const b64 = {
encode: array => btoa(String.fromCharCode.apply(null, array)),
decode: str => Uint8Array.from(atob(str), c => c.charCodeAt(0))
};
async function generateKeyPair() {
const keyPair = await window.crypto.subtle.generateKey(
{
name: 'X25519',
namedCurve: 'X25519',
},
true,
['deriveKey', 'deriveBits']
);
const privateKey = await window.crypto.subtle.exportKey('raw', keyPair.privateKey);
const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey);
return {
privateKey: b64.encode(new Uint8Array(privateKey)),
publicKey: b64.encode(new Uint8Array(publicKey))
};
}
document.addEventListener('DOMContentLoaded', async function() {
const form = document.getElementById('subscription-form');
const slider = document.getElementById('duration-slider');
const durationDisplay = document.getElementById('duration-display');
const priceDisplay = document.getElementById('price-display');
const presetButtons = document.querySelectorAll('.duration-preset');
const userIdInput = document.getElementById('user-id');
const publicKeyInput = document.getElementById('public-key');
const regenerateButton = document.getElementById('regenerate-keys');
let currentKeyPair = null;
function formatDuration(hours) {
if (hours < 24) return `${hours} hour${hours === 1 ? '' : 's'}`;
if (hours < 168) return `${hours / 24} day${hours === 24 ? '' : 's'}`;
if (hours < 720) return `${Math.floor(hours / 168)} week${hours === 168 ? '' : 's'}`;
return `${Math.floor(hours / 720)} month${hours === 720 ? '' : 's'}`;
}
async function generateNewKeys() {
try {
currentKeyPair = await generateKeyPair();
publicKeyInput.value = currentKeyPair.publicKey;
// Save private key to localStorage
const keyData = {
privateKey: currentKeyPair.privateKey,
publicKey: currentKeyPair.publicKey,
createdAt: new Date().toISOString()
};
localStorage.setItem(`vpn_keys_${userIdInput.value}`, JSON.stringify(keyData));
} catch (error) {
console.error('Failed to generate keys:', error);
alert('Failed to generate WireGuard keys. Please try again.');
}
}
async function initializeForm() {
// Generate user ID
userIdInput.value = crypto.randomUUID();
// Generate initial keys
await generateNewKeys();
}
async function updatePrice(hours) {
try {
const response = await fetch('/api/calculate-price', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hours: parseInt(hours) })
});
const data = await response.json();
priceDisplay.textContent = data.price;
durationDisplay.textContent = formatDuration(hours);
} catch (error) {
console.error('Error calculating price:', error);
}
}
// Event listeners
slider.addEventListener('input', () => updatePrice(slider.value));
regenerateButton.addEventListener('click', generateNewKeys);
presetButtons.forEach(button => {
button.addEventListener('click', (e) => {
const hours = e.target.dataset.hours;
slider.value = hours;
updatePrice(hours);
});
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (!currentKeyPair) {
alert('No keys generated. Please refresh the page.');
return;
}
try {
const response = await fetch('/create-invoice', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
duration: parseInt(slider.value),
userId: userIdInput.value,
publicKey: currentKeyPair.publicKey
})
});
if (!response.ok) {
throw new Error('Failed to create invoice');
}
const data = await response.json();
window.location.href = data.checkout_url;
} catch (error) {
console.error('Error creating invoice:', error);
alert('Failed to create payment invoice. Please try again.');
}
});
// Initialize the form
await initializeForm();
// Initial price calculation
updatePrice(slider.value);
});