Настройка приёма платежей в Solana (SOL)
Специфика Solana-платежей в том, что здесь нет нативного стандарта "платёжного запроса" уровня BIP-21 (Bitcoin) или EIP-681 (Ethereum). Зато есть Solana Pay — открытый протокол, разработанный Solana Labs совместно с экосистемой, покрывающий большинство payment use cases: простые SOL-переводы, SPL-токены, транзакционные запросы с произвольной логикой на бэкенде.
Solana Pay: Transfer Request
Простейший сценарий — запрос на перевод фиксированной суммы SOL или SPL-токена. URL-схема:
solana:<recipient>?amount=<value>&label=<label>&message=<message>&memo=<memo>
Пример для оплаты 10 USDC:
solana:7Np41RSSZFkBmBUkVBntbRMzEnbZBqVkjFEf3BQnkGTi
?amount=10
&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
&label=MyShop
&message=Order%20%2312345
&memo=ORDER-12345
spl-token — mint address USDC на mainnet. memo — произвольная строка, записывается в транзакцию on-chain через Memo program, используется для reconciliation заказов.
Этот URL кодируется в QR-код. Кошельки с поддержкой Solana Pay (Phantom, Solflare, Backpack) автоматически разбирают его и показывают детали платежа.
Генерация QR на бэкенде
import {
encodeURL,
createQR,
TransferRequestURLFields,
} from "@solana/pay";
import { PublicKey } from "@solana/web3.js";
import BigNumber from "bignumber.js";
function createPaymentRequest(orderId: string, amountUSDC: number) {
const recipient = new PublicKey("YOUR_MERCHANT_WALLET");
const splToken = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
const urlFields: TransferRequestURLFields = {
recipient,
amount: new BigNumber(amountUSDC),
splToken,
label: "MyShop",
message: `Order #${orderId}`,
memo: orderId,
};
const url = encodeURL(urlFields);
const qrCode = createQR(url, 360, "transparent");
return { url: url.toString(), qrCode };
}
Верификация платежа
После показа QR нужно подтвердить факт оплаты. Solana Pay предоставляет findReference — поиск транзакции по reference pubkey (уникальный ключ, добавляемый в каждый платёж):
import { findReference, validateTransfer } from "@solana/pay";
import { Connection, clusterApiUrl, Keypair } from "@solana/web3.js";
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
// reference — уникальный keypair для каждого заказа
const reference = Keypair.generate().publicKey;
// Polling каждые 2 секунды
async function pollForPayment(reference: PublicKey, expectedAmount: BigNumber) {
while (true) {
try {
const signatureInfo = await findReference(connection, reference, {
finality: "confirmed",
});
await validateTransfer(connection, signatureInfo.signature, {
recipient: MERCHANT_WALLET,
amount: expectedAmount,
splToken: USDC_MINT,
reference,
});
return signatureInfo.signature; // платёж подтверждён
} catch (e) {
// FindReferenceError — транзакция ещё не появилась, ждём
await new Promise((r) => setTimeout(r, 2000));
}
}
}
validateTransfer проверяет: правильный recipient, правильная сумма, правильный токен. Если что-то не совпадает — бросает исключение. Это защита от попытки оплатить "похожей" транзакцией.
Finality в Solana
Solana не имеет probabilistic finality как Bitcoin. Здесь есть уровни:
-
processed— транзакция включена в блок, может быть rollback при форке -
confirmed— блок получил >66% голосов супербольшинства -
finalized— блок в rooted части цепи, rollback невозможен
Для платежей используйте confirmed — это баланс скорости (~2–3 сек) и безопасности. finalized добавляет ещё ~5–10 сек, оправдано только для крупных сумм.
Что нужно для полноценной реализации
- Merchant wallet (лучше hardware wallet или multisig через Squads Protocol)
- Эндпоинт создания платёжного запроса с уникальным reference
- WebSocket или polling для мониторинга подтверждения
- Сохранение
signatureтранзакции в базе для аудита - Обработка timeout (Solana Pay рекомендует 2 мин, после — показывать ошибку)
- RPC endpoint: не публичный
api.mainnet-beta.solana.comдля production — он throttled; использовать Helius, QuickNode или Alchemy







