Регистрация: пополнение в том же окне и тестовый top-up

This commit is contained in:
AidarKC
2026-08-11 20:19:46 +04:00
parent 4b0a934e51
commit 95bbd2852e
51 changed files with 67 additions and 2445 deletions
@@ -257,8 +257,8 @@ export function render({ navigate }) {
topupButton.textContent = 'Пополнить кошелёк';
topupButton.addEventListener('click', async () => {
try {
const walletAddress = await deriveUserWalletAddress();
window.open(getTopupSiteUrl(walletAddress), '_blank', 'noopener,noreferrer');
await deriveUserWalletAddress();
navigate('topup-view');
} catch (error) {
status.className = 'status-line is-unavailable';
status.textContent = `Не удалось подготовить кошелёк: ${error?.message || 'unknown'}`;
@@ -301,10 +301,8 @@ export function render({ navigate }) {
: `Для регистрации пополните этот кошелёк соланами примерно на ${RECOMMENDED_TOPUP_SOL} SOL. Сейчас на кошельке ${formatSol(currentBalance, 6)} SOL. Минимум для продолжения: ${MIN_REQUIRED_SOL} SOL.`;
status.style.display = '';
if (isTestContour) {
const openTopup = window.confirm('Открыть страницу пополнения с вашим кошельком?');
if (openTopup) {
window.open(getTopupSiteUrl(walletAddress), '_blank', 'noopener,noreferrer');
}
const openTopup = window.confirm('Перейти на экран пополнения и затем продолжить регистрацию?');
if (openTopup) navigate('topup-view');
}
return;
}
+41 -18
View File
@@ -1,14 +1,18 @@
import { renderHeader } from '../components/header.js';
import { state } from '../state.js';
import {
createSolanaWalletFromPrivateBase58,
formatSol,
getBalanceSol,
getTopupSiteUrl,
requestAirdropSol,
transferSol,
} from '../services/solana-wallet-service.js';
import { loadSolanaWeb3 } from '../vendor/solana-web3-loader.js';
export const pageMeta = { id: 'topup-view', title: 'Пополнение счета', showAppChrome: false };
export const pageMeta = { id: 'topup-view', title: 'Пополнение solana счета', showAppChrome: false };
const DEVNET_ENDPOINT = 'https://api.devnet.solana.com';
const SENDER_PRIVATE_32_BASE58 = '6xqAuKYvA8qrCdAkcw7Y8aMgvBnYk8JLxWLma5BzbAvu';
const REGISTRATION_TOPUP_AMOUNT_SOL = 0.02;
// Канонический Solana-адрес пополнения = публичный device-ключ из сгенерированного набора ключей.
// Тот же путь, что в registration-payment-view (deriveUserWalletAddress); не выводим адрес
@@ -39,6 +43,10 @@ export function render({ navigate }) {
const status = document.createElement('p');
status.className = 'meta-muted';
status.textContent = 'Проверяем кошелек...';
status.style.whiteSpace = 'pre-wrap';
status.style.overflowWrap = 'anywhere';
status.style.wordBreak = 'break-word';
status.style.fontSize = '13px';
const copyButton = document.createElement('button');
copyButton.className = 'ghost-btn';
@@ -63,42 +71,54 @@ export function render({ navigate }) {
const card = document.createElement('div');
card.className = 'card stack';
card.innerHTML = `
<p class="auth-copy">Кнопка «Пополнить» в кошельке будет переводить на отдельный сайт. Пока доступно тестовое пополнение.</p>
<div class="stack" style="gap:6px;">
<p class="meta-muted">1. Вы можете открыть промо-сайт пополнения с подставленным кошельком.</p>
<p class="meta-muted">2. Либо нажать «Тестовое пополнение» и получить 1 SOL через DevNet airdrop.</p>
</div>
<a class="link-card" id="topup-site-link" href="${getTopupSiteUrl(state.registrationPayment.walletAddress || '')}" target="_blank" rel="noreferrer">Открыть сайт пополнения</a>
<p class="auth-copy">Можете или пополнить счёт тестовыми соланами и продолжить регистрацию.</p>
<div class="card stack" style="padding:12px; max-width:320px;">
<div class="field-label" style="margin-bottom:6px;">Кошелёк для пополнения (client key = Solana wallet)</div>
</div>
<div class="stack" style="gap:6px;">
<p class="meta-muted">Или можете отдельно открыть страницу тестового пополнения.</p>
<a class="link-card" id="topup-site-link" href="${getTopupSiteUrl(state.registrationPayment.walletAddress || '')}" target="_blank" rel="noreferrer">Открыть страницу тестового пополнения</a>
</div>
`;
card.children[3].append(walletRow);
card.children[1].append(walletRow);
const testButton = document.createElement('button');
testButton.className = 'ghost-btn';
testButton.type = 'button';
testButton.textContent = 'Тестовое пополнение (1 SOL)';
testButton.textContent = 'Тестовое пополнение для регистрации';
testButton.addEventListener('click', async () => {
const address = String(walletValue.value || '').trim();
if (!address) {
window.alert('Адрес кошелька не найден.');
return;
}
if (!senderKeypair) {
status.textContent = 'Ошибка: тестовый кошелёк отправителя ещё не инициализирован.';
return;
}
testButton.disabled = true;
try {
const drop = await requestAirdropSol({
endpoint: state.entrySettings.solanaServer,
address,
amountSol: 1,
status.textContent = 'Отправляем тестовое пополнение для регистрации...';
const tx = await transferSol({
endpoint: DEVNET_ENDPOINT,
fromKeypair: senderKeypair,
toAddress: address,
amountSol: REGISTRATION_TOPUP_AMOUNT_SOL,
});
const bal = await getBalanceSol({
endpoint: state.entrySettings.solanaServer,
endpoint: DEVNET_ENDPOINT,
address,
});
state.registrationPayment.balanceSOL = String(bal.sol);
status.textContent = `Тестовое пополнение выполнено. Новый баланс: ${formatSol(bal.sol, 6)} SOL. Signature: ${drop.signature}`;
status.style.fontSize = '12px';
status.textContent = [
'Тестовое пополнение для регистрации выполнено.',
`Кошелёк пополнен на ${REGISTRATION_TOPUP_AMOUNT_SOL} SOL.`,
`Новый баланс: ${formatSol(bal.sol, 6)} SOL.`,
`Signature: ${tx.signature}`,
].join('\n');
} catch (error) {
status.style.fontSize = '13px';
status.textContent = `Ошибка тестового пополнения: ${error?.message || 'unknown'}`;
} finally {
testButton.disabled = false;
@@ -108,12 +128,13 @@ export function render({ navigate }) {
const backButton = document.createElement('button');
backButton.className = 'primary-btn';
backButton.type = 'button';
backButton.textContent = 'Назад';
backButton.textContent = 'Продолжить регистрацию';
backButton.addEventListener('click', () => navigate('registration-payment-view'));
const actions = document.createElement('div');
actions.className = 'auth-footer-actions';
actions.append(testButton, backButton);
let senderKeypair = null;
(async () => {
try {
@@ -132,6 +153,8 @@ export function render({ navigate }) {
});
state.registrationPayment.balanceSOL = String(balance.sol);
status.textContent = `Текущий баланс: ${formatSol(balance.sol, 6)} SOL`;
const sender = await createSolanaWalletFromPrivateBase58(SENDER_PRIVATE_32_BASE58);
senderKeypair = sender.keypair;
} catch (error) {
status.textContent = `Не удалось получить баланс: ${error?.message || 'unknown'}`;
}
@@ -139,7 +162,7 @@ export function render({ navigate }) {
screen.append(
renderHeader({
title: 'Пополнение счета',
title: 'Пополнение solana счета',
leftAction: { label: '←', onClick: () => navigate('registration-payment-view') },
}),
card,